Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.ruby > #6558 > unrolled thread

what's up with return *splat ?

Started byPhlip <phlip2005@gmail.com>
First post2012-06-12 11:48 -0700
Last post2012-06-24 16:27 -0700
Articles 9 — 2 participants

Back to article view | Back to comp.lang.ruby


Contents

  what's up with return *splat ? Phlip <phlip2005@gmail.com> - 2012-06-12 11:48 -0700
    Re: what's up with return *splat ? Robert Klemme <shortcutter@googlemail.com> - 2012-06-12 23:19 +0200
      Re: what's up with return *splat ? Phlip <phlip2005@gmail.com> - 2012-06-12 17:10 -0700
        Re: what's up with return *splat ? Robert Klemme <shortcutter@googlemail.com> - 2012-06-16 15:04 +0200
          Re: what's up with return *splat ? Phlip <phlip2005@gmail.com> - 2012-06-21 16:24 -0700
            Re: what's up with return *splat ? Robert Klemme <shortcutter@googlemail.com> - 2012-06-22 18:24 +0200
              Re: what's up with return *splat ? Phlip <phlip2005@gmail.com> - 2012-06-22 10:02 -0700
                Re: what's up with return *splat ? Robert Klemme <shortcutter@googlemail.com> - 2012-06-22 23:36 +0200
                  Re: what's up with return *splat ? Phlip <phlip2005@gmail.com> - 2012-06-24 16:27 -0700

#6558 — what's up with return *splat ?

FromPhlip <phlip2005@gmail.com>
Date2012-06-12 11:48 -0700
Subjectwhat's up with return *splat ?
Message-ID<0fae13bb-3468-4883-b855-17950bee1117@t2g2000pbl.googlegroups.com>
Rubies:

Back in the Halcyon days (whatever that means) of Ruby 1.8, a function
could obey the contract "return nil, a scalar, or an array" with a
mere splat:

  return * splat

The interpretation there is (roughly!) "splat behaves as if you had
written each argument in an array as scalars separated by commas."

So (roughly!), you would get one of these three results:

  return nil
  return scalar
  return [ scalar1, scalar2, scalar3 ]

Now that I work in Ruby 1.9, the splat don't work like that. It just
passes through an array.

Am I using it wrong? Did splat change? If so, why? And can I use some
other 1.9-compliant trick?

--
  Phlip
  http://zeekland.webcomics-x.com/2011/08/03/  <-- Tigga, please...

[toc] | [next] | [standalone]


#6559

FromRobert Klemme <shortcutter@googlemail.com>
Date2012-06-12 23:19 +0200
Message-ID<a3pq3mFm27U1@mid.individual.net>
In reply to#6558
On 12.06.2012 20:48, Phlip wrote:
> Rubies:
>
> Back in the Halcyon days (whatever that means) of Ruby 1.8, a function
> could obey the contract "return nil, a scalar, or an array" with a
> mere splat:
>
>    return * splat
>
> The interpretation there is (roughly!) "splat behaves as if you had
> written each argument in an array as scalars separated by commas."
>
> So (roughly!), you would get one of these three results:
>
>    return nil
>    return scalar
>    return [ scalar1, scalar2, scalar3 ]
>
> Now that I work in Ruby 1.9, the splat don't work like that. It just
> passes through an array.
>
> Am I using it wrong? Did splat change? If so, why? And can I use some
> other 1.9-compliant trick?

Can you please show the code you used for testing?  Thank you.

Kind regards

	robert


-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#6560

FromPhlip <phlip2005@gmail.com>
Date2012-06-12 17:10 -0700
Message-ID<2a77ca16-20d5-4700-bf7d-0a746f4730d6@e7g2000pbg.googlegroups.com>
In reply to#6559
> Can you please show the code you used for testing?  Thank you.

My assert_latest() stopped working. Here's an independent test case:

p RUBY_VERSION

def assert(x);  x or raise 'broke!';  end

def splat(*wat); return *wat;  end

assert nil == splat()
assert 'yo' == splat('yo')
assert ['yo', 'dude'] == splat('yo', 'dude')

assert [] == splat()
assert ['yo'] == splat('yo')
assert ['yo', 'dude'] == splat('yo', 'dude')

The top three assertions pass in 1.8.7, and the bottom three pass in
1.9.2.

[toc] | [prev] | [next] | [standalone]


#6567

FromRobert Klemme <shortcutter@googlemail.com>
Date2012-06-16 15:04 +0200
Message-ID<a43eidFbv2U1@mid.individual.net>
In reply to#6560
On 13.06.2012 02:10, Phlip wrote:
>> Can you please show the code you used for testing?  Thank you.
>
> My assert_latest() stopped working. Here's an independent test case:
>
> p RUBY_VERSION
>
> def assert(x);  x or raise 'broke!';  end
>
> def splat(*wat); return *wat;  end
>
> assert nil == splat()
> assert 'yo' == splat('yo')
> assert ['yo', 'dude'] == splat('yo', 'dude')
>
> assert [] == splat()
> assert ['yo'] == splat('yo')
> assert ['yo', 'dude'] == splat('yo', 'dude')
>
> The top three assertions pass in 1.8.7, and the bottom three pass in
> 1.9.2.

I get different results:

$ ruby x.rb
"1.8.7"
broke! x.rb:12
broke! x.rb:13
$ ruby19 x.rb
"1.9.3"
broke! x.rb:8:in `<main>'
broke! x.rb:9:in `<main>'
$ cat -n x.rb
      1
      2  p RUBY_VERSION
      3
      4  def assert(x);  x or warn "broke! #{caller[0]}";  end
      5
      6  def splat(*wat); return *wat;  end
      7
      8  assert nil == splat()
      9  assert 'yo' == splat('yo')
     10  assert ['yo', 'dude'] == splat('yo', 'dude')
     11
     12  assert [] == splat()
     13  assert ['yo'] == splat('yo')
     14  assert ['yo', 'dude'] == splat('yo', 'dude')
     15
     16  a,b=splat(1,2)
     17  assert a == 1
     18  assert b == 2
     19
     20  a,b=splat(1)
     21  assert a == 1
     22  assert b == nil
     23
     24  a,b=splat
     25  assert a == nil
     26  assert b == nil

And the interesting bits (lines 16ff) are treated identical.

I think you use a function in one of two ways: either you expect one 
result and that can be nil or not, or you expect multiple replies and 
assign them to different variables which can either be nil or not.

What practical use case is impaired by the difference?

Kind regards

	robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#6570

FromPhlip <phlip2005@gmail.com>
Date2012-06-21 16:24 -0700
Message-ID<9040131a-0ad1-4736-a0e3-693b9cf58a27@g4g2000pbn.googlegroups.com>
In reply to#6567
Tx for the experiment; it confirmed mine.

> I think you use a function in one of two ways: either you expect one
> result and that can be nil or not, or you expect multiple replies and
> assign them to different variables which can either be nil or not.
>
> What practical use case is impaired by the difference?

When I write an assertion, I know the ordinality of the expected
result.

  note1, note2 = assert_latest User.notes do
    User.create_notes()
  end

  assert{ user.notes == [note1, note2] }

  frob = assert_latest Frob do
    production_code_creating_frobs()
  end

  assert{ frob.member == 42 }

I want the assertions to break, with simple syntax errors, if the
ordinality is wrong. If assert_latest only returned arrays, that would
cause clutter like this:

  frobs = assert_latest Frob do
    production_code_creating_frobs()
  end

  assert{ frobs.count == 1 and frobs[0].member == 42 }

Instead of testing the count, I want to simply use the result as if
it's what I expect, and then fail as early as possible if it isn't.

When I first asked this question (on these very newsgroups IIRC), I
was directed to return *records. That solved the ton of crud required
to put the ordinality check _inside_ assert_latest().

Then return *splat's behavior changed in 1.9.2.

--
  Phlip
  http://elaborateart.webcomics-x.com/2011/09/15/

[toc] | [prev] | [next] | [standalone]


#6573

FromRobert Klemme <shortcutter@googlemail.com>
Date2012-06-22 18:24 +0200
Message-ID<a4jkhpFss0U1@mid.individual.net>
In reply to#6570
On 22.06.2012 01:24, Phlip wrote:
> Tx for the experiment; it confirmed mine.

Strange.  I said

> I get different results:
> ...

>> I think you use a function in one of two ways: either you expect one
>> result and that can be nil or not, or you expect multiple replies and
>> assign them to different variables which can either be nil or not.
>>
>> What practical use case is impaired by the difference?
>
> When I write an assertion, I know the ordinality of the expected
> result.
>
>    note1, note2 = assert_latest User.notes do
>      User.create_notes()
>    end
>
>    assert{ user.notes == [note1, note2] }
>
>    frob = assert_latest Frob do
>      production_code_creating_frobs()
>    end
>
>    assert{ frob.member == 42 }
>
> I want the assertions to break, with simple syntax errors, if the
> ordinality is wrong.

You cannot get syntax errors for this.  This is checked at runtime and 
not at parse time.

> If assert_latest only returned arrays, that would
> cause clutter like this:
>
>    frobs = assert_latest Frob do
>      production_code_creating_frobs()
>    end
>
>    assert{ frobs.count == 1 and frobs[0].member == 42 }
>
> Instead of testing the count, I want to simply use the result as if
> it's what I expect, and then fail as early as possible if it isn't.

Note that you can do this in 1.9.* and 1.8.7 (the comma):

irb(main):002:0> def f(*a) a end
=> nil
irb(main):003:0> f 1,2
=> [1, 2]
irb(main):004:0> x=f 1,2
=> [1, 2]
irb(main):005:0> x
=> [1, 2]
irb(main):006:0> x,=f 1,2
=> [1, 2]
irb(main):007:0> x
=> 1

Kind regards

	robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#6575

FromPhlip <phlip2005@gmail.com>
Date2012-06-22 10:02 -0700
Message-ID<3a1bf78f-70a5-4856-8fea-6e23671eaa72@y3g2000pbc.googlegroups.com>
In reply to#6573
On Jun 22, 9:24 am, Robert Klemme <shortcut...@googlemail.com> wrote:
> On 22.06.2012 01:24, Phlip wrote:
>
> > Tx for the experiment; it confirmed mine.
>
> Strange.  I said

Your experiment confirmed 1.9.2 changed the behavior, like mine did.

> > I want the assertions to break, with simple syntax errors, if the
> > ordinality is wrong.
>
> You cannot get syntax errors for this.  This is checked at runtime and
> not at parse time.

I'm aware of the definition of syntax error.

> irb(main):006:0> x,=f 1,2

That line does not die, with an-error-checked-at-runtime, nor would
x.member (given [] does not have .member) die with an-error-checked-at-
runtime.

When the author of a test writes assert_latest() they know whether
they expect a scalar or a list to return, and they should not waste
their time checking for a single-element list before using it in the
assertion that actually checks for the important stuff.

If I use record, = assert_latest(), and if a future bug added a second
record, the assertion would not break, that line would not break (with
a comma), and lines using record would not break.

Also I'm not sure why I need to justify the use case, if such a low-
level syntactical thing should not change in a language revision. It's
a bug in return *splat; it no longer behaves the same as = *splat.

[toc] | [prev] | [next] | [standalone]


#6576

FromRobert Klemme <shortcutter@googlemail.com>
Date2012-06-22 23:36 +0200
Message-ID<a4k6roF84oU1@mid.individual.net>
In reply to#6575
On 22.06.2012 19:02, Phlip wrote:
> On Jun 22, 9:24 am, Robert Klemme <shortcut...@googlemail.com> wrote:
>> On 22.06.2012 01:24, Phlip wrote:
>>
>>> Tx for the experiment; it confirmed mine.
>>
>> Strange.  I said
>
> Your experiment confirmed 1.9.2 changed the behavior, like mine did.

Yes, but I got different results than those you claimed.  Which made me 
wonder.

>>> I want the assertions to break, with simple syntax errors, if the
>>> ordinality is wrong.
>>
>> You cannot get syntax errors for this.  This is checked at runtime and
>> not at parse time.
>
> I'm aware of the definition of syntax error.

Why then do you use it in the wrong way?

>> irb(main):006:0> x,=f 1,2
>
> That line does not die, with an-error-checked-at-runtime, nor would
> x.member (given [] does not have .member) die with an-error-checked-at-
> runtime.
>
> When the author of a test writes assert_latest() they know whether
> they expect a scalar or a list to return, and they should not waste
> their time checking for a single-element list before using it in the
> assertion that actually checks for the important stuff.

Well, if you use "x,=..." then you have the first element and no 
additional checking is needed.  But if you really want to ensure the 
proper number of values is returned you need to test for the array 
length anyway.  At least you would need to to something like

irb(main):008:0> x,y,*remainder = f 1,2
=> [1, 2]
irb(main):009:0> remainder
=> []
irb(main):010:0> remainder.empty?
=> true
irb(main):011:0> x,y,*remainder = f 1,2,3
=> [1, 2, 3]
irb(main):012:0> remainder.empty?
=> false

For consistent checking of the returned data you can do

*x = f(...)

x will be an Array even if f returns a single value only.

> If I use record, = assert_latest(), and if a future bug added a second
> record, the assertion would not break, that line would not break (with
> a comma), and lines using record would not break.
>
> Also I'm not sure why I need to justify the use case, if such a low-
> level syntactical thing should not change in a language revision.

Actually most people seem to cope pretty well with this as only corner 
cases are affected of the change.  If you want to ensure no additional 
values are returned you need to splat assign any way to count values, i.e.

*x = f()

> It's
> a bug in return *splat; it no longer behaves the same as = *splat.

It's not syntax but runtime behavior.  You can and will never get a 
syntax error for this.

AFAIK Matz changed it deliberately - although I have to confess I don't 
remember the reasoning.  You'll probably find it in the archives.

Note though that also argument assignment changed quite a bit in 1.9 
which has much more sophisticated options where 1.8 only allowed for the 
last parameter to collect additional values.  The 1.9 model is superior 
to that:

irb(main):001:0> def f(a, *b, c) p a,b,c end
=> nil
irb(main):002:0> f 1
ArgumentError: wrong number of arguments (1 for 2)
         from (irb):1:in `f'
         from (irb):2
         from /usr/local/bin/irb19:12:in `<main>'
irb(main):003:0> f 1,2
1
[]
2
=> [1, [], 2]
irb(main):004:0> f 1,2,3
1
[2]
3
=> [1, [2], 3]
irb(main):005:0> f 1,2,3,4,5
1
[2, 3, 4]
5
=> [1, [2, 3, 4], 5]

And you have pattern matching with block arguments

irb(main):007:0> f = lambda {|a, (b,c), d| p a,b,c,d}
=> #<Proc:0x202a96c4@(irb):7 (lambda)>
irb(main):008:0> f[1]
ArgumentError: wrong number of arguments (1 for 3)
         from (irb):7:in `block in irb_binding'
         from (irb):8:in `[]'
         from (irb):8
         from /usr/local/bin/irb19:12:in `<main>'
irb(main):009:0> f[1,2]
ArgumentError: wrong number of arguments (2 for 3)
         from (irb):7:in `block in irb_binding'
         from (irb):9:in `[]'
         from (irb):9
         from /usr/local/bin/irb19:12:in `<main>'
irb(main):010:0> f[1,2,3]
1
2
nil
3
=> [1, 2, nil, 3]
irb(main):011:0> f[1,[2],3]
1
2
nil
3
=> [1, 2, nil, 3]
irb(main):012:0> f[1,[2,3],4]
1
2
3
4
=> [1, 2, 3, 4]
irb(main):013:0> f[1,[2,3,4],5]
1
2
3
5
=> [1, 2, 3, 5]


Cheers

	robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#6579

FromPhlip <phlip2005@gmail.com>
Date2012-06-24 16:27 -0700
Message-ID<306067e8-6297-4584-9021-42fbf6f944f9@po9g2000pbb.googlegroups.com>
In reply to#6576
> Actually most people seem to cope pretty well with this as only corner
> cases are affected of the change.  If you want to ensure no additional
> values are returned you need to splat assign any way to count values, i.e.
>
> *x = f()

Sorry, I can't handle this level of talking-past each other.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.ruby


csiph-web