Jwan622
Jwan622

Reputation: 11639

Ruby blocks and procs. Do you not need to specify a second argument when using a Proc?

I read this about Ruby Procs:

class Monkey

  # Monkey.tell_ape { "ook!" }
  # ape: ook!
  #  => nil
  def self.tell_ape
    tell("ape", &Proc.new)
  end

  def self.tell(name)
    puts "#{name}: #{yield}"
  end
end

In that above example... the block is being passed to .tell_ape and Proc.new is converting the block to a proc right? However... doesn't the .tell method need to accept a &block parameter in its method signature? We're passing a Proc as an argument to the .tell method right? If so... doesn't the .tell method need to have a second parameter?

Upvotes: 0

Views: 85

Answers (2)

Jörg W Mittag
Jörg W Mittag

Reputation: 369438

All methods always take exactly one optional block argument:

puts('Hello') { I.can.write.whatever.i.want.here.because.the.block.is.never.ran }
# Hello

def foo; end
foo { same.here }

The whole point of blocks is that they are syntactically and semantically lightweight, and one of the ways this is achieved is that they are anonymous:

def bar; yield if block_given? end
# Note how I didn't need to say where to yield to and what block to check since 
# there can only ever be at most one anyway.

bar { puts 'Hello' }
# Hello

They are also not objects.

But if it doesn't have a name and isn't even an object, then how can you refer to it? Well, that's where & parameters come in: they tell Ruby to convert the block to a proper Proc object and bind it to a parameter:

def baz(&blk) p blk end
baz { again.we.are.not.calling.the.block }
# #<Proc:0xdeadbeef081542@(pry):42>
# ^^^^^^^ Debugging representation of a Proc object

Procs have a call method which executes them (there's also a piece of syntactic sugar for calling call):

def quux(grault, &blk)
  blk.(grault)
  yield grault
end

quux('Hello') {|garply| puts garply }
# Hello
# Hello

Upvotes: 2

Sebastian
Sebastian

Reputation: 2204

When you have a method that receives a block, the yield keyword will execute the block, you don't have to pass it explicitly as an argument.

def foo
 yield 1
end

is equivalent to:

def foo(&block)
  block.call(1)
end

Upvotes: 1

Related Questions