Passing a named function instead of a code block?

Discussion in 'Ruby' started by Paul Jungwirth, Mar 20, 2009.

  1. Hello,

    I have a question about ruby's feature that when you call a method, you
    can pass in a block of code after the last argument. Instead of writing
    a code block, suppose I already have a def'd method that would work just
    as well. Is there any way I can pass that in directly? For example,
    suppose I have this code:

    #!/usr/bin/env ruby

    def fib(n)
    a, b = 0, 1
    n.times do |i|
    a, b = b, a+b
    end
    b
    end

    c = [1, 2, 3, 4]

    puts c.collect {|i| fib i}

    That will print fib(1), fib(2), fib(3), fib(4). But why write a code
    block that takes one argument and does nothing but call a function that
    takes one argument? Is there some way I could have replaced the last
    line with something like this?:

    puts c.collect \fib

    In python I could have written the last line thus:

    print map(fib, (1, 2, 3, 4))

    Does ruby have something similar?

    Thanks,
    Paul
    --
    Posted via http://www.ruby-forum.com/.
     
    Paul Jungwirth, Mar 20, 2009
    #1
    1. Advertisements

  2. Paul Jungwirth wrote:
    > I have a question about ruby's feature that when you call a method, you
    > can pass in a block of code after the last argument. Instead of writing
    > a code block, suppose I already have a def'd method that would work just
    > as well. Is there any way I can pass that in directly? For example,
    > suppose I have this code:
    >
    > #!/usr/bin/env ruby
    >
    > def fib(n)
    > a, b = 0, 1
    > n.times do |i|
    > a, b = b, a+b
    > end
    > b
    > end
    >
    > c = [1, 2, 3, 4]
    >
    > puts c.collect {|i| fib i}


    puts c.collect(&method:)fib))

    -Matthias
    --
    Posted via http://www.ruby-forum.com/.
     
    Matthias Reitinger, Mar 20, 2009
    #2
    1. Advertisements

  3. Paul Jungwirth

    7stud -- Guest

    Matthias Reitinger wrote:
    >
    > puts c.collect(&method:)fib))
    >


    Why is there a difference here:

    def square1(x)
    x*x
    end

    square2 = lambda { |x| x*x}

    puts [1, 2, 3].collect(&square2)
    puts [1, 2, 3].collect(&square1)

    --output:--
    1
    4
    9
    r1test.rb:8:in `square1': wrong number of arguments (0 for 1)
    (ArgumentError)
    from r1test.rb:8


    Why does ruby make you use the tortured syntax:

    &method:)square1)

    for a method vs. the easier syntax for a Proc object?


    --
    Posted via http://www.ruby-forum.com/.
     
    7stud --, Mar 20, 2009
    #3
  4. Paul Jungwirth

    James Coglan Guest

    [Note: parts of this message were removed to make it a legal post.]

    2009/3/20 7stud -- <>

    > Matthias Reitinger wrote:
    > >
    > > puts c.collect(&method:)fib))
    > >

    >
    > Why is there a difference here:
    >
    > def square1(x)
    > x*x
    > end
    >
    > square2 = lambda { |x| x*x}
    >
    > puts [1, 2, 3].collect(&square2)
    > puts [1, 2, 3].collect(&square1)
    >
    > --output:--
    > 1
    > 4
    > 9
    > r1test.rb:8:in `square1': wrong number of arguments (0 for 1)
    > (ArgumentError)
    > from r1test.rb:8
    >
    >
    > Why does ruby make you use the tortured syntax:
    >
    > &method:)square1)
    >
    > for a method vs. the easier syntax for a Proc object?




    square2 is a variable name (ie. something you've made an assignment to),
    it's just a reference to the lambda object. However, square1 is a method and
    Ruby allows calling methods without parens, so 'square1' is actually
    interpreted as a method call to square1 with no arguments. Therefore, to
    grab a method as an object without calling it, we need to use
    method:)square1).

    --
    James Coglan
    http://github.com/jcoglan
     
    James Coglan, Mar 20, 2009
    #4
  5. Paul Jungwirth

    7stud -- Guest

    James Coglan wrote:
    > 2009/3/20 7stud -- <>
    >
    >>
    >> (ArgumentError)
    >> from r1test.rb:8
    >>
    >>
    >> Why does ruby make you use the tortured syntax:
    >>
    >> &method:)square1)
    >>
    >> for a method vs. the easier syntax for a Proc object?

    >
    >
    >
    > square2 is a variable name (ie. something you've made an assignment to),
    > it's just a reference to the lambda object. However, square1 is a method
    > and
    > Ruby allows calling methods without parens, so 'square1' is actually
    > interpreted as a method call to square1 with no arguments. Therefore, to
    > grab a method as an object without calling it, we need to use
    > method:)square1).


    Ok. But there is a certain amount of hypocrisy in that explanation
    Look here:

    &square1
    :square1

    In the first expression there is a method call, and in the second there
    isn't. Yet, you could describe both those lines as: a method name
    preceded by some symbol.



    --
    Posted via http://www.ruby-forum.com/.
     
    7stud --, Mar 20, 2009
    #5
  6. Paul Jungwirth

    James Coglan Guest

    [Note: parts of this message were removed to make it a legal post.]

    2009/3/20 7stud -- <>

    > James Coglan wrote:
    > > 2009/3/20 7stud -- <>
    > >
    > >>
    > >> (ArgumentError)
    > >> from r1test.rb:8
    > >>
    > >>
    > >> Why does ruby make you use the tortured syntax:
    > >>
    > >> &method:)square1)
    > >>
    > >> for a method vs. the easier syntax for a Proc object?

    > >
    > >
    > >
    > > square2 is a variable name (ie. something you've made an assignment to),
    > > it's just a reference to the lambda object. However, square1 is a method
    > > and
    > > Ruby allows calling methods without parens, so 'square1' is actually
    > > interpreted as a method call to square1 with no arguments. Therefore, to
    > > grab a method as an object without calling it, we need to use
    > > method:)square1).

    >
    > Ok. But there is a certain amount of hypocrisy in that explanation
    > Look here:
    >
    > &square1
    > :square1
    >
    > In the first expression there is a method call, and in the second there
    > isn't. Yet, you could describe both those lines as: a method name
    > preceded by some symbol.



    Yes, it probably looks that way. To see the difference it helps to know how
    Ruby is parsed. :square1 is an atomic unit representing the symbol whose
    name is 'square1'. ":" is not an operator, it is part of the syntax for
    symbols. However, "&" is an operator responsible for casting between procs
    and blocks. The expression '&square1' should be read '&( square1 )', that is
    we call square1 and cast the result of that using '&'. The same applies to
    'method'. 'method' is a method that takes a symbol/string and returns the
    Method object with that name in the current scope. 'method(square1)' would
    be interpreted as a call to square1, passing the result to 'method'.

    So, '&square1' throws an error because you're calling a method with
    insufficient arguments. '&:square1' would try to cast a symbol to a proc,
    which if you're using ActiveSupport would return the block { |object|
    object.square1 }.

    '&square2' is fine as square2 is just a variable referring to a proc.
    Likewise, '&method:)square1)' is fine because method:)square1) is a Method
    object, which can be cast to a block.

    --
    James Coglan
    http://github.com/jcoglan
     
    James Coglan, Mar 20, 2009
    #6
  7. Matthias Reitinger wrote:
    > puts c.collect(&method:)fib))


    Thank you for your help; this does just what I wanted.
    --
    Posted via http://www.ruby-forum.com/.
     
    Paul Jungwirth, Mar 20, 2009
    #7
  8. Okay, now how about when you want to reference a method on an instance?
    For example:

    #!/usr/bin/env ruby

    class Adder
    def initialize(n)
    @n = n
    end

    def use(x)
    x + n
    end
    end


    a = Adder.new(5)

    print [1, 2, 3, 4].collect(&method:)a.use))

    This doesn't work. It looks like : binds tighter than .. But :(a.use)
    doesn't work either. Any suggestions?

    --
    Posted via http://www.ruby-forum.com/.
     
    Paul Jungwirth, Mar 20, 2009
    #8
  9. Paul Jungwirth

    James Coglan Guest

    [Note: parts of this message were removed to make it a legal post.]

    2009/3/20 Paul Jungwirth <>

    > Okay, now how about when you want to reference a method on an instance?
    > For example:
    >
    > #!/usr/bin/env ruby
    >
    > class Adder
    > def initialize(n)
    > @n = n
    > end
    >
    > def use(x)
    > x + n
    > end
    > end
    >
    >
    > a = Adder.new(5)
    >
    > print [1, 2, 3, 4].collect(&method:)a.use))
    >
    > This doesn't work. It looks like : binds tighter than .. But :(a.use)
    > doesn't work either. Any suggestions?



    a.method:)use)

    See docs for Object#method:
    http://ruby-doc.org/core/classes/Object.html#M000338
     
    James Coglan, Mar 20, 2009
    #9
  10. James Coglan wrote:
    > See docs for Object#method:
    > http://ruby-doc.org/core/classes/Object.html#M000338


    Ah, so I want a line like this:

    puts [1, 2, 3, 4].collect(&a.method:)use))

    Actually, this helps me understand the previous solution. I guess
    method:)foo) and a.method:)foo) are actually going to the same place,
    huh? And each return a Method object, which I then cast to a block with
    &. It is all starting to make sense....

    Doesn't this seem like an appropriate place for some syntactic sugar?
    I'd be nice if you could abbreviate such calls with something like:
    \:foo
    \:a.foo
    Since ruby is so focused on closures, this seems like an appropriate
    place for such brevity.

    Thanks again!


    --
    Posted via http://www.ruby-forum.com/.
     
    Paul Jungwirth, Mar 20, 2009
    #10
  11. Paul Jungwirth

    7stud -- Guest

    James Coglan wrote:
    > 2009/3/20 7stud -- <>
    >
    >> >> &method:)square1)
    >> > grab a method as an object without calling it, we need to use

    >> preceded by some symbol.

    > Yes, it probably looks that way. To see the difference it helps to know
    > how
    > Ruby is parsed. :square1 is an atomic unit representing the symbol whose
    > name is 'square1'. ":" is not an operator, it is part of the syntax for
    > symbols. However, "&" is an operator responsible for casting between
    > procs
    > and blocks. The expression '&square1' should be read '&( square1 )',
    > that is
    > we call square1 and cast the result of that using '&'. The same applies
    > to
    > 'method'. 'method' is a method that takes a symbol/string and returns
    > the
    > Method object with that name in the current scope. 'method(square1)'
    > would
    > be interpreted as a call to square1, passing the result to 'method'.
    >
    > So, '&square1' throws an error because you're calling a method with
    > insufficient arguments. '&:square1' would try to cast a symbol to a
    > proc,
    > which if you're using ActiveSupport would return the block { |object|
    > object.square1 }.
    >
    > '&square2' is fine as square2 is just a variable referring to a proc.
    > Likewise, '&method:)square1)' is fine because method:)square1) is a
    > Method
    > object, which can be cast to a block.


    Thanks for the explanation. You said that (&) casts between block and
    Procs. But (&) also appears to be casting a Method to a block in this
    line:

    puts c.collect(&method:)fib))

    (&) can also be used to cast in the reverse direction, e.g. from a block
    to a Proc:

    def test(&aProc)
    aProc.call(10)
    end

    test {|x| puts x}

    --output:--
    10


    Can (&) cast a block to a Method?

    --
    Posted via http://www.ruby-forum.com/.
     
    7stud --, Mar 22, 2009
    #11
  12. On Mar 22, 2009, at 4:20 AM, 7stud -- wrote:

    > James Coglan wrote:
    >
    > Thanks for the explanation. You said that (&) casts between block and
    > Procs. But (&) also appears to be casting a Method to a block in this
    > line:
    >
    > puts c.collect(&method:)fib))
    >
    > (&) can also be used to cast in the reverse direction, e.g. from a
    > block
    > to a Proc:
    >
    > def test(&aProc)
    > aProc.call(10)
    > end
    >
    > test {|x| puts x}
    >
    > --output:--
    > 10
    >
    >
    > Can (&) cast a block to a Method?



    There are no casts in Ruby.

    & in a Method call calls #to_proc on an Object and expects it to
    return a proc. Thats a conversion that has to be done explicitly by
    the programmer.

    & in a Method definition indicates that the argument has to be a Proc
    or something that responds to #to_proc (or a passed Block) which then
    gets enforced.

    Blocks are Procs (AFAIK, internally, Methods are basically procs as
    well). The only magic is "yield" together with a block, which to my
    knowledge doesn't require the construction of a Proc Object that has
    to be visible to the programmer. But thats up to the implementation.

    Regards,
    Florian

    --
    Florian Gilcher

    smtp:
    jabber:
    gpg: 533148E2
     
    Florian Gilcher, Mar 22, 2009
    #12
  13. On 3/21/09, 7stud -- <> wrote:
    > James Coglan wrote:
    >> 2009/3/20 7stud -- <>
    >>
    >>> >> &method:)square1)
    >>> > grab a method as an object without calling it, we need to use
    >>> preceded by some symbol.

    >> Yes, it probably looks that way. To see the difference it helps to know
    >> how
    >> Ruby is parsed. :square1 is an atomic unit representing the symbol whose
    >> name is 'square1'. ":" is not an operator, it is part of the syntax for
    >> symbols. However, "&" is an operator responsible for casting between
    >> procs
    >> and blocks. The expression '&square1' should be read '&( square1 )',
    >> that is
    >> we call square1 and cast the result of that using '&'. The same applies
    >> to
    >> 'method'. 'method' is a method that takes a symbol/string and returns
    >> the
    >> Method object with that name in the current scope. 'method(square1)'
    >> would
    >> be interpreted as a call to square1, passing the result to 'method'.
    >>
    >> So, '&square1' throws an error because you're calling a method with
    >> insufficient arguments. '&:square1' would try to cast a symbol to a
    >> proc,
    >> which if you're using ActiveSupport would return the block { |object|
    >> object.square1 }.
    >>
    >> '&square2' is fine as square2 is just a variable referring to a proc.
    >> Likewise, '&method:)square1)' is fine because method:)square1) is a
    >> Method
    >> object, which can be cast to a block.

    >
    > Thanks for the explanation. You said that (&) casts between block and
    > Procs. But (&) also appears to be casting a Method to a block in this
    > line:
    >
    > puts c.collect(&method:)fib))


    Actually, its casting a Method to a Proc (by calling to_proc on it)
    and then treating the proc as a block. To see that's what is
    happening, try this:

    class Integer
    def to_proc
    Proc.new { self }
    end
    end

    (1..10).map(&1)

    > Can (&) cast a block to a Method?


    No. & in a method call always treats a the given proc as a block given
    to the method while & in a method definition always makes a block
    given to the method as a Proc. Its probably best not to think of this
    as "casting" except insofar as the first one "casts" to a proc first
    by calling to_proc; blocks aren't objects, and if you do this:

    def consume(object=nil)
    if block_given? then yield else object end
    end

    p = lambda { puts "foo!" }
    consume(&p)

    You don't get the results you' d get if an object of the
    (non-existent) class "Block" was passed to consume, you get the
    results you would get if consume was called with a block. So thinking
    of & as "casting" an argument to a different class in the method call
    is misleading.
     
    Christopher Dicely, Mar 22, 2009
    #13
    1. Advertisements

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. morrell
    Replies:
    1
    Views:
    1,113
    roy axenov
    Oct 10, 2006
  2. Alvin
    Replies:
    8
    Views:
    1,140
  3. Hendrik van Rooyen
    Replies:
    10
    Views:
    1,440
    Grant Edwards
    Aug 24, 2007
  4. Shea Martin

    passing block to another function

    Shea Martin, Mar 20, 2006, in forum: Ruby
    Replies:
    5
    Views:
    151
    Joel VanderWerf
    Mar 20, 2006
  5. Brian Candler

    Passing a block to a block

    Brian Candler, Nov 4, 2008, in forum: Ruby
    Replies:
    2
    Views:
    143
    Brian Candler
    Nov 4, 2008
Loading...

Share This Page