Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ruby blocks not first-class

From a language design perspective, why aren't ruby blocks first-class?

Similarly, I think blocks should actually be lambdas, thereby getting rid of the need for cumbersome syntax such as proc {...}.call or &proc or lambda or Proc.new. This would get rid of the need for yield too.

like image 883
Francis Haart Avatar asked Jul 13 '11 21:07

Francis Haart


People also ask

Does Ruby have first class functions?

Ruby doesn't have first-class functions, but it does have closures in the form of blocks, procs and lambdas. Blocks are used for passing blocks of code to methods, and procs and lambda's allow storing blocks of code in variables.

What are Ruby blocks?

Blocks are a handy and powerful feature in Ruby, and we can use them anywhere. Blocks are anonymous pieces of code that accept input from arguments and return a value.

What are procs in Ruby?

A Proc object is an encapsulation of a block of code, which can be stored in a local variable, passed to a method or another Proc, and can be called. Proc is an essential concept in Ruby and a core of its functional programming features.

What is Ruby block used for?

A ruby block is one or more lines of code that you put inside the do and end keywords (or { and } for inline blocks). It allows you to group code into a standalone unit that you can use as a method argument.


1 Answers

From a language design perspective, why aren't ruby blocks first-class?

Mostly for performance reasons, in as far as I'm aware. Consider:

def test_yield
  yield
end

def test_block &block
  block.call
end

la = lambda {}

def test_lambda l
  l.call
end

Then, benchmark with an empty block for the first two, vs the third with a new la per call or with the same la, and note how much faster the yield goes in each case. The reason is, the explicit &block variable creates a Proc object, as does lambda, while merely yielding doesn't.

A side-effect (which I've actually found uses for, to recursively pipe passed blocks through the use of a proc object), is you cannot yield in a proc or lambda outside some kind of enclosing scope:

foo = proc { yield if block_given? }
foo.call { puts 'not shown' }

def bar
  baz = proc { yield if block_given? }
  baz.call
end

bar { puts 'should show' }

This is because, as I've come to understand it (I lost a lot of hair due to this, until it ticked), block_given? is sent to main when foo calls it, and to bar rather that baz when it gets evaluated in bar.

like image 62
Denis de Bernardy Avatar answered Nov 01 '22 14:11

Denis de Bernardy