ruby blocks not first-class
Asked Answered
A

3

7

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.

Arterial answered 13/7, 2011 at 21:18 Comment(3)
Would blocks be, in your opinion, first-class in Ruby if class Proc was named Block instead?Seagrave
@Mladen Jablanović Sure, there is something to be said for syntax (i.e. if {return o.x} made a thunk). What I was asking, though, is: what do blocks buy us?Arterial
Blocks are just a syntax, a convenient way to pass a Proc to a method. Procs are first-class in Ruby. I'm not sure what would you expect when you say "blocks should be first-class". Can you clarify?Seagrave
E
6

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.

Enemy answered 14/7, 2011 at 0:4 Comment(5)
Hmmm, I ran each a million times and this is what i got: test_yield: 0.5090551 seconds... test_block: 1.667427751 seconds... test_lambda: 0.741804596 seconds... So I doubt blocks are for efficiency. Also, seeing as ruby's already obscenely slow, I'd rather have me a nice semantics.Arterial
You forgot a test: lambda created on the fly on each call, which should give similar results as the block case. And yield is nice semantics best I've played with it.Enemy
Ah yes I intentionally left that out, the cost being object creation, and unlike the &block idiom, explicit. Sigh, lambdas have enough overhead due to nested environments. They shouldn't be objects too. Matz was Nutz.Arterial
Actually, the &block idiom is just as explicit as a lambda. It's fully equivalent to: def foo; block = Proc.new; block.call(); end. And nearly equivalent to the same with a lambda. (The only differences between proc and lambda, as point out by banister, are the pickiness when it comes to arity and how return statements are processed.)Enemy
Re your point on Matz, I'm not sure what you're hoping for... You asked why blocks weren't first class objects; the widely accepted reason is performance per my suggestion. There isn't much point in criticizing the language or its creator on top. There's plenty of choice out there if you don't like Ruby. :-)Enemy
O
2

lambda and proc (and block) have different semantics. Procs/blocks have non-local returns and are less picky about arity; lambdas are more method-like in their behaviour. In my opinion this distinction is useful and procs/blocks/lambdas should NOT be unified as you suggest.

Occident answered 14/7, 2011 at 1:22 Comment(2)
yeah, i found this semantic "dissection" both enlightening and hilarious: github.com/dmichael/closures-in-ruby. nonlocal return might be useful, but also seems insidious.Arterial
@Francis Haart: non-local returns are brilliant for emulating macros: my_if(cond) { return }Occident
W
0

Ruby methods are not functions or first-class citizens because they cannot be passed to other methods as arguments, returned by other methods, or assigned to variables. Ruby procs are first-class, similar to JavaScript’s first-class functions

The following code demonstrates how Ruby methods cannot be stored in variables or returned from methods and therefore do not meet the ‘first-class’ criteria:

class Dog
  def speak
    'ruff'
  end
end

fido = Dog.new
# Ruby methods cannot be stored in variables
# Methods are executed and variables only store values
x = fido.speak
# x stores the method's return value, not the method itself
x # => 'ruff'

# Methods cannot return other methods
# Methods can only return values from other methods
def hi
  Dog.new.speak
end
# hi returns the method's return value, not the method itself
hi # => 'ruff'

a programming language is said to have first-class functions if it treats functions as first-class citizens. Specifically, this means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.

Weightlessness answered 5/2, 2020 at 10:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.