Why "super" works differently with yield and block parameter in Ruby?

Hi,a = Class.new do
  def foo
    yield
  end
end

b = Class.new(a) do
  def foo
    super{
      "b"
    }
  end
end

p b.new.foo{"c"} # => "b"

c = Class.new do
  def foo(&block)
    block.call
  end
end

d = Class.new(c) do
  def foo(&block)
    block = -> { "b" }
    super
  end
end

p d.new.foo{"c"} # => "c"I was expecting the last one to give the output as "b". But why is it "c" ?
Regards,
Arup Rakshit

c = Class.new do
  def foo(&block)
    block.call
  end
end

d = Class.new(c) do
  def foo(&block)
    block = -> { "b" }
    super
  end
end

p d.new.foo{"c"} # => "c"I was expecting the last one to give the output as "b". But why is it "c" ?Actual confusion came from this :
a = Class.new do def foo(arg) puts "value from superclass #{arg}" endendb = Class.new(a) do def foo(arg) arg = "b" super puts "value from superclass #{arg}" endendb.new.foo("c") # => "value from superclass b"#---- The above is expected, but the below is unexpected as per the above resultc = Class.new do def foo(&block) block.call p block.object_id endendd = Class.new(c) do def foo(&block) block = -> { "b" } super block.object_id endendd.new.foo{"c"} # => "c"

Question is - Why block variable is being treated as special variable ? Regards,
Arup Rakshit

You have to explicitly pass the block:

2.0.0-p195 :022 > d = Class.new(c) do
2.0.0-p195 :023 > def foo(&block)
2.0.0-p195 :024?> block = -> { "b" }
2.0.0-p195 :025?> super &block
2.0.0-p195 :026?> end
2.0.0-p195 :027?> end
=> #<Class:0x00000001d00808>
2.0.0-p195 :028 > p d.new.foo{"c"}
"b"

Jesus.

···

On Fri, Jan 16, 2015 at 12:57 PM, Arup Rakshit <aruprakshit@rocketmail.com> wrote:

Hi,

a = Class.new do
  def foo
    yield
  end
end

b = Class.new(a) do
  def foo
    super{
      "b"
    }
  end
end

p b.new.foo{"c"} # => "b"

c = Class.new do
  def foo(&block)
    block.call
  end
end

d = Class.new(c) do
  def foo(&block)
    block = -> { "b" }
    super
  end
end

p d.new.foo{"c"} # => "c"

I was expecting the last one to give the output as "b". But why is it "c" ?

I was expecting the last one to give the output as "b". But why is it "c" ?Actual confusion came from this :

a = Class.new do
  def foo(arg)
    puts "value from superclass #{arg}"
  end
end

b = Class.new(a) do
  def foo(arg)
    arg = "b"
    super
    puts "value from superclass #{arg}"
  end
end
b.new.foo("c") # => "value from superclass b"

#---- The above is expected, but the below is unexpected as per the above result

c = Class.new do
  def foo(&block)
    block.call
    p block.object_id
  end
end

d = Class.new(c) do
  def foo(&block)
    block = -> { "b" }
    super
  end
end
d.new.foo{"c"} # => "c"

Question is - Why block variable is being treated as special variable ?

···

On Friday, January 16, 2015 12:14:23 PM Arup Rakshit wrote:

c = Class.new do
  def foo(&block)
    block.call
  end
end

d = Class.new(c) do
  def foo(&block)
    block = -> { "b" }
    super
  end
end

p d.new.foo{"c"} # => "c"

--

Regards,
Arup Rakshit

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

--Brian Kernighan

Hi Arup,

Without arguments, super passes the same (actual) "things" that came
as argument to the current method call.
No matter what name you give to it (in this case, "block").
So, making the variable (name of the thing) to point to a different
thing (-> { "b"}) doesn't affect what super knows about the actual
"thing" that was passed as parameter.

Got it?

Best regards and keep rubying,
Abinoam Jr.

···

On Sun, Jan 18, 2015 at 1:47 PM, Arup Rakshit <aruprakshit@rocketmail.com> wrote:

On Friday, January 16, 2015 12:14:23 PM Arup Rakshit wrote:

c = Class.new do
  def foo(&block)
    block.call
  end
end

d = Class.new(c) do
  def foo(&block)
    block = -> { "b" }
    super
  end
end

p d.new.foo{"c"} # => "c"

I was expecting the last one to give the output as "b". But why is it "c" ?Actual confusion came from this :

a = Class.new do
  def foo(arg)
    puts "value from superclass #{arg}"
  end
end

b = Class.new(a) do
  def foo(arg)
    arg = "b"
    super
    puts "value from superclass #{arg}"
  end
end
b.new.foo("c") # => "value from superclass b"

#---- The above is expected, but the below is unexpected as per the above result

c = Class.new do
  def foo(&block)
    block.call
    p block.object_id
  end
end

d = Class.new(c) do
  def foo(&block)
    block = -> { "b" }
    super
  end
end
d.new.foo{"c"} # => "c"

Question is - Why block variable is being treated as special variable ?

--

Regards,
Arup Rakshit

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

--Brian Kernighan

I suspect that 'super' uses the call stack, while the variable 'block' uses
local scope, so modifying one does not modify the other.

However, writing back into the object passed in as a parameter (and thus
also on the call stack) would affect both, because object references.

This is pure conjecture. Caveat emptor.

···

On 19 January 2015 at 02:47, Arup Rakshit <aruprakshit@rocketmail.com> wrote:

On Friday, January 16, 2015 12:14:23 PM Arup Rakshit wrote:
>
> c = Class.new do
> def foo(&block)
> block.call
> end
> end
>
> d = Class.new(c) do
> def foo(&block)
> block = -> { "b" }
> super
> end
> end
>
> p d.new.foo{"c"} # => "c"

I was expecting the last one to give the output as "b". But why is it "c" ?

--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

Hi Abinoam,

In the first example, you can see the **super** sent the current value of the argument variable **arg**.

In the second example, **super** was sending the previous value of the variable **block**, even if it has been given different value before the **super** call.

-- These 2 observations are contradicting.

···

On Sunday, January 18, 2015 11:06:20 PM Abinoam Jr. wrote:

Hi Arup,

Without arguments, super passes the same (actual) "things" that came
as argument to the current method call.
No matter what name you give to it (in this case, "block").
So, making the variable (name of the thing) to point to a different
thing (-> { "b"}) doesn't affect what super knows about the actual
"thing" that was passed as parameter.

Got it?

Best regards and keep rubying,
Abinoam Jr.

--

Regards,
Arup Rakshit

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

--Brian Kernighan

Hi Arup,

I'm pretty surprised. (I shouldn't be... more on it later)
It happens that what I said in my previous message was _completely_ wrong.
Sorry for that!

#!/usr/bin/env ruby

class A
  def foo(arg_1, arg_2)
    puts "At superclass arg_1: #{arg_1} and arg_2: #{arg_2}"
  end
end

class B < A
  def foo(arg_1, arg_2)
    puts "At son class (before changing) arg_1: #{arg_1} and arg_2: #{arg_2}"
    super
    arg_1 = "Modified 1"
    arg_2 = "Modified 2"
    puts "At son class (_after_ changing) arg_1: #{arg_1} and arg_2: #{arg_2}"
    super
  end
end

B.new.foo("UNmodified 1", "UNmodified 2")

Results:

At son class (before changing) arg_1: UNmodified 1 and arg_2: UNmodified 2
At superclass arg_1: UNmodified 1 and arg_2: UNmodified 2
At son class (_after_ changing) arg_1: Modified 1 and arg_2: Modified 2
At superclass arg_1: Modified 1 and arg_2: Modified 2

It surprises me because of:

···

===
From Programming Ruby 1.9 and 2.0 4th edition - Dave Thomas, Chad
Fowler and Andy Hunt

super
super ‹ ( ‹, param›*‹, *array › ) ›‹block›
Within the body of a method, a call to super acts like a call to the
original method, except that
the search for a method body starts in the superclass of the object
that contained the original
method. If no parameters (and no parentheses) are passed to super, the
original method’s
parameters will be passed; otherwise, the parameters to super will be passed.

===
From Well Grounded Rubyist 2nd Edition - David A. Black

The way super handles arguments is as follows:
■ Called with no argument list (empty or otherwise), super
automatically forwards
the arguments that were passed to the method from which it’s called.
■ Called with an empty argument list—super() —super sends no arguments to
the higher-up method, even if arguments were passed to the current method.
■ Called with specific arguments—super(a,b,c) —super sends exactly those
arguments.
This unusual treatment of arguments exists because the most common
case is the first
one, where you want to bump up to the next-higher method with the same arguments
as those received by the method from which super is being called.
That case is given
the simplest syntax—you just type super . (And because super is a
keyword rather than
a method, it can be engineered to provide this special behavior.)

BUT... (the drummer drums...)

Matz said...

===
The Ruby Programming Language (2008) - David Flanagan and Yukihiro Matsumoto

If you use super as a bare keyword—with no arguments and no
parentheses—then all
of the arguments that were passed to the current method are passed to
the superclass
method. Note, however, that it’s the current values of the method
parameters that are
passed to the superclass method. If the method has modified the values
in its parameter
variables, then the modified values are passed to the invocation of
the superclass
method.

REPEATING: "If the method has modified the values in its parameter
variables, then the modified values are passed to the invocation of
the superclass
method."

That's it.

And... about your block variable question. It seams that &block is
converting the passed block again into the block variable of the super
class. I mean... you didn't passed any args at all. You just passed a
block. The ampersand did the magic of converting the block into the
"block" variable at the son class ("b"). So it does it again at the
superclass ("a"). Just a guess.

Look at these 2 modified versions of your code.
Note that we define #foo on 'c' without any block variable (parameter).
If "d" was passing the block as an argument, it would raise an
ArgumentError because the c's #foo doesn't expect any!
So, I think it's passing as a block, the original block.

c = Class.new do
  def foo # without any parameters
    puts "block given #{block_given?}"
    yield
  end
end

d = Class.new(c) do
  def foo(&block)
    puts "son class: block_id #{block.object_id}"
    super
    block = -> { puts "Modified Block" }
    puts "son class: (after assignment) block_id #{block.object_id}"
    super
  end
end

d.new.foo{ puts "Block" }

Results:
son class: block_id 70365808831120
block given true
Block
son class: (after assignment) block_id 70365808830900
block given true
Block

c = Class.new do
  def foo(&block)
    puts "block given #{block_given?}"
    puts "superclass: block_id #{block.object_id}"
    block.call
  end
end

d = Class.new(c) do
  def foo(&block)
    puts "son class: block_id #{block.object_id}"
    super
    block = -> { puts "Modified Block" }
    puts "son class: (after assignment) block_id #{block.object_id}"
    super
  end
end

d.new.foo{ puts "Block" }

son class: block_id 70221470263680
block given true
superclass: block_id 70221470263680
Block
son class: (after assignment) block_id 70221470263400
block given true
superclass: block_id 70221470263680
Block

Thanks Arup for bringing this question that I have passed unoticed.
Today I know more Ruby than yesterday!!!

Abinoam Jr.

On Mon, Jan 19, 2015 at 12:41 AM, Arup Rakshit <aruprakshit@rocketmail.com> wrote:

On Sunday, January 18, 2015 11:06:20 PM Abinoam Jr. wrote:

Hi Arup,

Without arguments, super passes the same (actual) "things" that came
as argument to the current method call.
No matter what name you give to it (in this case, "block").
So, making the variable (name of the thing) to point to a different
thing (-> { "b"}) doesn't affect what super knows about the actual
"thing" that was passed as parameter.

Got it?

Best regards and keep rubying,
Abinoam Jr.

Hi Abinoam,

In the first example, you can see the **super** sent the current value of the argument variable **arg**.

In the second example, **super** was sending the previous value of the variable **block**, even if it has been given different value before the **super** call.

-- These 2 observations are contradicting.

--

Regards,
Arup Rakshit

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

--Brian Kernighan

​When you run b.new.foo("c") you should see two outputs (c, then b). Do you?

···

On 19 January 2015 at 13:41, Arup Rakshit <aruprakshit@rocketmail.com> wrote:

In the first example, you can see the **super** sent the current value of
the argument variable **arg**.

In the second example, **super** was sending the previous value of the
variable **block**, even if it has been given different value before the
**super** call.

-- These 2 observations are contradicting.

--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

In the first example, you can see the **super** sent the current value of the argument variable **arg**.

In the second example, **super** was sending the previous value of the variable **block**, even if it has been given different value before the **super** call.

-- These 2 observations are contradicting.

​When you run b.new.foo("c") you should see two outputs (c, then b). Do you?
​Hi Matthew,
No. It is always "b". This part is ok as per the documentation. Confusion is with `block`, it is working just reverse way of this example.
a = Class.new do def foo(arg) puts "value from superclass #{arg}" endend
b = Class.new(a) do def foo(arg) arg = "b" super puts "value from superclass #{arg}" endendb.new.foo("c")# >> value from superclass b# >> value from superclass b

···

On 19 January 2015 at 13:41, Arup Rakshit <aruprakshit@rocketmail.com> wrote:

Guys, when you use super as a bareword you get passed up the *current*,
*local* value of the parameters, not the original values. That's the whole
point in this thread, that Ruby does not seem to treat block parameters the
same way in that regard.

The question is NOT how to be able to get the value from the new block,
question is why is Ruby acting differently for blocks.

It is a formal question about the way Ruby works (and I don't know the
answer).

Guys, when you use super as a bareword you get passed up the *current*,
*local* value of the parameters, not the original values.

This, for me, is the unexpected one of the two.

That's the whole
point in this thread, that Ruby does not seem to treat block parameters the
same way in that regard.

The inconsistency looks like a bug, but I don't know the answer either.

The question is NOT how to be able to get the value from the new block,
question is why is Ruby acting differently for blocks.

It is a formal question about the way Ruby works (and I don't know the
answer).

Me neither.

Jesus.

···

On Mon, Jan 19, 2015 at 9:25 AM, Xavier Noria <fxn@hashref.com> wrote:

Oh. That's not what I'd have expected. Odd. Presumably one of the
behaviours is a bug.

It's worth noting that the block is passed differently than other args. See
rb_block_given_p, rb_yield, and similar functions. Probably related to
that. There might be an old discussion in the bug tracker.

···

On 19 January 2015 at 17:15, Arup Rakshit <aruprakshit@rocketmail.com> wrote:


Hi Matthew,

No. It is always "b". This part is ok as per the documentation. Confusion
is with `block`, it is working just reverse way of this example.

a = Class.new do
  def foo(arg)
    puts "value from superclass #{arg}"
  end
end

b = Class.new(a) do
  def foo(arg)
    arg = "b"
    super
    puts "value from superclass #{arg}"
  end
end
b.new.foo("c")
# >> value from superclass b
# >> value from superclass b

--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

Hi Arup,

REPEATING: "If the method has modified the values in its parameter
variables, then the modified values are passed to the invocation of
the superclass
method."

That's it.

That's most direct explanation about the bare word `super`.

And... about your block variable question. It seams that &block is
converting the passed block again into the block variable of the super
class. I mean... you didn't passed any args at all. You just passed a
block. The ampersand did the magic of converting the block into the
"block" variable at the son class ("b"). So it does it again at the
superclass ("a"). Just a guess.

Look at these 2 modified versions of your code.
Note that we define #foo on 'c' without any block variable (parameter).
If "d" was passing the block as an argument, it would raise an
ArgumentError because the c's #foo doesn't expect any!
So, I think it's passing as a block, the original block.

c = Class.new do
  def foo # without any parameters
    puts "block given #{block_given?}"
    yield
  end
end

d = Class.new(c) do
  def foo(&block)
    puts "son class: block_id #{block.object_id}"
    super
    block = -> { puts "Modified Block" }
    puts "son class: (after assignment) block_id #{block.object_id}"
    super
  end
end

d.new.foo{ puts "Block" }

Results:
son class: block_id 70365808831120
block given true
Block
son class: (after assignment) block_id 70365808830900
block given true
Block

That's very good experiment. Nice example, I didn't experiment this far. :slight_smile:

c = Class.new do
  def foo(&block)
    puts "block given #{block_given?}"
    puts "superclass: block_id #{block.object_id}"
    block.call
  end
end

d = Class.new(c) do
  def foo(&block)
    puts "son class: block_id #{block.object_id}"
    super
    block = -> { puts "Modified Block" }
    puts "son class: (after assignment) block_id #{block.object_id}"
    super
  end
end

d.new.foo{ puts "Block" }

son class: block_id 70221470263680
block given true
superclass: block_id 70221470263680
Block
son class: (after assignment) block_id 70221470263400
block given true
superclass: block_id 70221470263680
Block

Thanks Arup for bringing this question that I have passed unoticed.
Today I know more Ruby than yesterday!!!

Abinoam Jr.

Thanks Abinoam for sharing these many information to the community. So, we can consider the _block_ version as an special case of *how super works* in Ruby. It would be good to know what's the reason of designing the block version of *super* bare-word functionality in this way.. Wanted to know, just out of curiosity. :slight_smile:

···

On Monday, January 19, 2015 02:56:51 PM Abinoam Jr. wrote:

--

Regards,
Arup Rakshit

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

--Brian Kernighan

To my mind, in:

    def foo(&b) end

the &b means "capture the block and convert it to a Proc object". So
'b' is more like a local variable than a parameter. E.g.:

    def foo
      b = block_given? ? block.to_proc : nil
    end

(except that my 'block' method doesn't exist)

Or to put it another way, a function's 'block' slot is different from
its 'parameters' slot. It's easy to modify the parameters, but hard to
modify the block. Note that 'yield' depends on being able to access
the block slot, independent of any function parameters.

···

On 25/01/2015, Arup Rakshit <aruprakshit@rocketmail.com> wrote:

It would be good to know what's the reason of designing the block
version of *super* bare-word functionality in this way.. Wanted to know,
just out of curiosity. :slight_smile:

--
  Matthew Kerwin
  http://matthew.kerwin.net.au/