In Ruby, is the if / elsif / else sub-block the same as the "block" that is passed as a parameter?

I read about if / elsif / else in Ruby and I ran into some differences in terminology when describing how control expressions work.

In the Ruby Programming Wikibooks (emphasis mine):

The conditional branch takes the result of the test expression and executes the block of code depending on whether the test expression is true or false.

and

If an if statement, for example, not only determines whether a subordinate block of code will be executed , but also results in the value itself.

Ruby-doc.org, however, does not mention blocks at all in the definitions:

The simplest if expression consists of two parts: the "test" expression and the "then" expression. ... If the expression "test" evaluates to true, then the expression "then" is evaluated.

Usually when I read about "blocks" in Ruby, it is almost always in the context of procs and lambdas. For example, rubylearning.com defines a block:

Ruby block is a way of grouping operators and can only be displayed in a source adjacent to a method call; block written starting at the same line as the last parameter of the method call (or the closing parenthesis of the parameter list).

Questions:

  • When talking about blocks of code in Ruby, are we talking about a group of code that is passed to a method, or are we just talking about a group of code in general?
  • Is there a way to easily distinguish between the two (and is there a technical difference between the two)?

The context for these questions: I'm interested in referring to code inside conditionals, as blocks will be confused with new Ruby programmers when they are later exposed to blocks, procs and lambdas.

+3


source to share


1 answer


TL; DR if...end

is an expression, not a block

The correct use of the term block

in Ruby is code passed to the method between do...end

or curly braces {...}

. A block can, and often is, implicitly converted to Proc

within a method using the syntax &block

in the method signature. This new one Proc

is an object with its own methods that can be passed to other methods, stored in variables and data structures, called multiple times, etc.

def block_to_proc(&block)
  prc = block
  puts prc
  prc.class
end

block_to_proc { 'inside the block' }
# "#<Proc:0x007fa626845a98@(irb):21>"
# => Proc

      

In the above code, a is Proc

implicitly created with a block as its body and assigned to a variable block

. Likewise, Proc

(or lambda

, type Proc

) can be "expanded" into blocks and passed to methods that expect them, using the syntax &block

at the end of the argument list.

def proc_to_block
  result = yield # only the return value of the block can be saved, not the block itself
  puts result
  result.class
end

block = Proc.new { 'inside the Proc' }

proc_to_block(&block)
# "inside the Proc"
# => String

      

Although there is a two-way street between block

and Proc

s, they don't match. Note that to define a, Proc

we need to pass block

in Proc.new

. Strictly speaking, block

it is just a piece of code passed to a method, the execution of which is deferred until an explicit call. A is Proc

defined with block

, its execution is also deferred until called, but it is a bonafide object like any other. A block

can't survive on its own, Proc

maybe.

On the other hand, block

or block of code

sometimes accidentally used to refer to any discrete piece of code enclosed in the Ruby keywords, ending in end

: if...else...end

, begin...rescue...end

, def...end

, class...end

, module...end

, until...end

. But they don't really block, in fact, and only actually resemble them on the surface. They often also have pending execution until some condition is met. But they can stand entirely on their own and always have return values. The use of the expression "expression" in Ruby-doc.org is more precise.

From wikipedia

An expression in a programming language is a combination of one or more explicit values, constants, variables, operators, and functions that the programming language interprets (according to its particular rules of precedence and association) and computes to produce ("to return", in a stateful environment ) other meaning.



This is why you can do things like this

return_value = if 'expression'
  true
end

return_value # => true

      

Try to do it with a block

return_value = do
  true
end

# SyntaxError: (irb):24: syntax error, unexpected keyword_do_block
# return_value = do
#                  ^

      

A block is not an expression in and of itself. This requires either yield

or switching to Proc

. What happens when we pass a block to a method that doesn't want it?

puts("indifferent") { "to blocks" }
# "indifferent"
# => nil

      

The block is completely lost, it disappears with no return value, no execution, as if it never existed. To complete the expression and get the return value is required yield

.

class Object
  def puts(*args)
    super
    yield if block_given?
  end
end

puts("mindful") { "of blocks" }
# "mindful"
# => "of blocks"

      

+1


source







All Articles