-5

I am looking at some code that is roughly:

foo = true
while foo
  foo = newfoo() do
    thing1()
  end
end

What is going on here? What does the do ... end block that follows the foo assignment do?

sawa
  • 165,429
  • 45
  • 277
  • 381
BobDoolittle
  • 1,326
  • 1
  • 12
  • 21
  • 3
    it's a block. http://stackoverflow.com/questions/4911353/best-explanation-of-ruby-blocks – Anthony Aug 13 '15 at 23:58
  • 1
    Holy duplicate. Just google for "ruby do end". – takendarkk Aug 13 '15 at 23:58
  • I *know* what do/end do. My question is about the strange place the block is starting, and whether that's significant. Is that block somehow conditional on the behavior of the assignment, or is it just sloppy coding? Should the do simply be on the next line by itself for clarity, or is that different syntax? – BobDoolittle Aug 14 '15 at 00:03
  • 1
    So you're asking what `newfoo` does? Shouldn't we be asking you that? You're the one who posted the code :) – eirikir Aug 14 '15 at 00:04
  • Is the execution of that block conditional on the behavior of the statement preceding it? Or if "do" were on a line by itself after the assignment would this behave identically? – BobDoolittle Aug 14 '15 at 00:05
  • 3
    What happens to a block depends entirely on the method that it gets passed to. `newfoo` receives the block and can call it or store it somewhere, or both, or neither – Gareth Aug 14 '15 at 00:06
  • Aha - perfect Gareth, thanks. If you make that an answer I will accept it. It was not clear to me that the block was being passed into the function. – BobDoolittle Aug 14 '15 at 00:08
  • 2
    The contents of the block may or may not have an effect on the return value of `newfoo`, and therefore may or may not affect the operation of the containing loop. Who knows? (Not us) – Gareth Aug 14 '15 at 00:08
  • You can accept Joanjo's answer, because his link points to all the relevant information. :) – Gareth Aug 14 '15 at 00:09

2 Answers2

2

It's a block. In Ruby exists blocks, procs and lambdas. Check this link to understand it better.

http://www.reactive.io/tips/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/

Juanjo Salvador
  • 1,073
  • 1
  • 12
  • 33
2

What you see is do...end block being passed to a function BEFORE assignment is being made. Remember code:

    [1,2].each do puts "xd" end

? Block "do puts "xd" end" is passed here to the "each" method.

Same in your code. The newfoo() function is being called with the block, the block is assumably yielded in newfoo() and then return value of newfoo() is assigned to foo.

You should however avoid such syntax, if the block is short- use {} instead of do..end, if it's not - consider using Proc.new instead.

Two code examples for you, both with the same result:

def newfoo()
    yield()
    return false
end
foo = true
while(foo)
    foo = newfoo() do
        puts "JP2GMD"
    end
end

And version with storing Proc

truth_to_be_spoken = Proc.new { puts "JP2GMD" }

def newfoo()
    yield()
    return false
end
foo = true
while(foo)
    foo = newfoo() {truth_to_be_spoken.call()}
end

Edit in response to comment

"{}" is certainly not preferable to "do ...end" in any other case than really short proc being passed (which was the case in example).

To why would you want to use proc instead of passing result parameter in real situations (note this are just examples to show syntax, not the real-world uses!): there are many reasons, now i can think of at least two simple examples:
1. You can use it to evaluate something lazily inside your function only if needed:

magic_number = Proc.new do
    x = # some really complicated code
    # it posts question on stackoverflow
    # to ask users, what number they would like to divide
    # waits an hour for answers 
    # and picks the one with most votes
    return x
end

def divide_numbers(divisor)
    if divisor == 0
        puts "Cannot into"
    else
        dividend = yield
        puts (dividend / divisor)
end
  1. You can use values that will be known inside function and pass them as parameters (which is in fact, how you use iteration methods!). Here is the code that uses that two times - parameter is passed with each to a block "{}" and inside "{}" you pass a parameter to a declared proc:

    second_power_double_printer = Proc.new do |param| puts param puts param end

    [1,2,3,4].each { |number| double_printer.call(number*number) }

To why use do..end/{} syntax instead of passing block as parameter. There might be more reasons i can't think of right know, but

  1. Built-in methods are implemented this way, so it's cleaner to stick to this.
  2. Do...end / {} will implicitly create anonymous block for you. You can't do it with parameter, for example:

    def f(proc) proc.call() end

    f(do puts "It's sad:(" end)

  3. If you use anonymous blocks you can make at least one mistake - forget to pass a block. If you use function parameters you can make at least two mistakes - forget to pass anything (although - if you're using IDE it would help you in this case) and pass something, that is not a block - resulting in "undefined method call" error.

piezol
  • 915
  • 6
  • 24
  • Why is "{ ... }" preferable to "do ... end"? – BobDoolittle Sep 01 '15 at 21:13
  • Why wouldn't it be better to define "def newfoo(block)" and have newfoo invoke "block.call()", then just pass truth_to_be_spoken into newfoo? That reads more clearly to me (it's more like other languages). What's the advantage to this (2nd) approach? – BobDoolittle Sep 01 '15 at 21:14