Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does super(&nil) do in ruby?

Tags:

ruby

I was reading the source code for concurrent-ruby, and came across this line of ruby code.

def initialize(*args, &block)
  super(&nil) # <--- ???
  synchronize { ns_initialize(*args, &block) }
end

Can someone explain to me what it is supposed to do?

like image 592
snw Avatar asked Jun 03 '18 20:06

snw


1 Answers

You have to start by understanding the & operator as it's used here. See for example:

# The & here converts a block argument to a proc
def a(&blk)
end

# The & here converts the proc to a block
a(&Proc.new { true })

In the proc => block case, it is also capable of turning some objects into procs, for example:

# The symbol :class gets to_proc called here
[1].map(&:class)

Symbol#to_proc produces the same functionality as follows

[1].map(&Proc.new { |x| x.class })

I'm not sure where the official documentation for this is (would welcome a pointer), but from testing it seems that &nil does not actually pass any block to the method at all - it has no effect:

def a
  block_given?
end

a {} # => true
a &:puts # => true
a &nil # => false

Now that that is explained, I can go on to say why it's needed.

If you omit parens with super, all arguments are passed:

class A
  def initialize arg
    puts arg && block_given?
  end
end

class B < A
  def initialize arg
    super
  end
end

B.new(1) {}
# prints "true" - block and arg were both passed to super

If you don't want this to happen, you can manually pass arguments to super. There is an issue with this, which I will get to after:

class A
  def initialize arg1, arg2=nil
    puts arg1 && !arg2
  end
end

class B < A
  def initialize arg1, arg2=nil
    super arg1
  end
end

B.new 1, 2
# prints "true" - arg1 was passed to super but not arg2

The problem is that although you can prevent positional and keyword args from being passed, this approach will not prevent a block being passed along:

class A
  def initialize arg1
    puts arg1 && block_given?
  end
end

class B < A
  def initialize arg1
    super arg1
  end
end

B.new(1) { }
# prints "true" - arg and block were both passed

For whatever reason, it is important here that it not happen, so they use an idiom I have not seen before but appears to get the job done: &nil. It's essentially saying "pass nothing as a block". I guess if you don't do this then blocks are automatically forwarded.

like image 190
max pleaner Avatar answered Oct 23 '22 14:10

max pleaner