Search code examples
rubymethod-referencepointfreefirst-class-functions

Ruby passing method


Trying to understand ruby's complexity, but makes no sense so far.

5.times(method(:puts))

Gives error, that doesn't make a lot of sense. Do I have some kind of syntax error or is it not possible to do in ruby?

ArgumentError: wrong number of arguments (given 1, expected 0)
        from (irb):78:in `times'

I am trying to do something similar to

[0, 1, 2, 3, 4].forEach(console.log)

and

java.util.stream.IntStream.range(0, 5).forEach(System.out::println);

At the same time these do work:

method(:puts).call(1)
# and
5.times { |i| puts i }

Solution

  • times takes a block argument, which is distinguished from "regular" arguments by an ampersand. You can either pass it an explicit block

    5.times { |x| puts x }
    

    or you can pass it a value with &

    5.times(&method(:puts))
    

    Having the block argument treated differently allows us to write methods that look and act a lot like built-in statements. For instance, an infinite loop in Ruby can be written as

    loop {
      # fun stuff happening in here
    }
    

    But loop is a method in the core library, not a built-in keyword. We could've written that loop function ourselves. Enumerable and other modules make heavy use of block arguments to provide friendlier syntax, which is one of the primary goals of Ruby as a language.