Passing method references in python & ruby

Hey all,

Kind of an idle, syntax comparison question here…

In python, you can apparently pass method references around very
easily. So imagine you have these 2 functions defined:

def firstWay(arg1, arg2)
return ‘Tastes great.’

def secondWay(arg1, arg2)
return ‘Less filling.’

Then you can define a method that takes a method name as an argument,
and calls it just by throwing parens (and any expected arguments of
course) after it.

def doStuff(whichway, first_arg, second_arg)
return whichway(first_arg, second_arg)

So calling:

puts doStuff(firstWay, None, None)

Would result in ‘Tastes great.’

What’s the most graceful way to do this sort of thing in ruby? I
tried passing in e.g., firstWay.to_proc, but that got me a complaint
about not having enough arguments on the call. I can imagine doing,
e.g.,

first_pointer = Proc.new do |foo, bar|
return firstWay(foo, bar)
end

One for each alternate method & passing one of those in. I can also
imagine having doStuff take a block that would call the desired
function & yielding out to that. But neither of those are as pretty
as the python I think. Are those my best options in ruby, or is there
another way?

Thanks!

-Roy

[email protected] wrote:

def secondWay(arg1, arg2)

  return firstWay(foo, bar)

-Roy

Here is another way:

irb(main):001:0> def dostuff(whichway, arg1, arg2)
irb(main):002:1> send(whichway, arg1, arg2)
irb(main):003:1> end
=> nil
irb(main):004:0> def first_way(arg1, arg2)
irb(main):005:1> “tastes great”
irb(main):006:1> end
=> nil
irb(main):007:0> def second_way(arg1, arg2)
irb(main):008:1> “less filling”
irb(main):009:1> end
=> nil
irb(main):010:0> dostuff(:first_way, nil, nil)
=> “tastes great”
irb(main):011:0> dostuff(:second_way, nil, nil)
=> “less filling”
irb(main):012:0> “No! #{dostuff(:first_way, nil, nil).capitalize}!”
=> “No! Tastes great!”

-Justin

2008/1/18, Justin C. [email protected]:

So calling:
first_pointer = Proc.new do |foo, bar|

-Roy

Here is another way:

irb(main):001:0> def dostuff(whichway, arg1, arg2)
irb(main):002:1> send(whichway, arg1, arg2)
irb(main):003:1> end
=> nil

#dostuff is really superfluous because the way you defined it it’s
just an alias for #send.

irb(main):011:0> dostuff(:second_way, nil, nil)
=> “less filling”
irb(main):012:0> “No! #{dostuff(:first_way, nil, nil).capitalize}!”
=> “No! Tastes great!”

So basically you would just do

send(:first_way, nil, nil)
send(:second_way, nil, nil)

However, if there are /many/ alternatives, then - depending on
circumstances - a Hash might be a better alternative:

algorithms = {
:first_way => lambda {|a,b| ‘Tastes great.’},
:second_way => lambda {|a,b| ‘Less filling’},

more algorithms here…

}

puts algorithms[:first_way][nil, nil]
puts algorithms[:second_way][nil, nil]

Kind regards

robert

In python when you use method name without parenthesises you get
method reference. In Ruby it won’t work beacuse method can be called
without parenthesises (and Ruby programmers love it :)). If you want
to get method reference you can use method(:you_method_name) and you
get reference to Method instance. Later you can just call ‘call’
method and pass some parameters. Direct traslation of your program to
Ruby looks like:

def firstWay(arg1, arg2)
return ‘Tastes great.’
end

def secondWay(arg1, arg2)
return ‘Less filling.’
end

def doStuff(whichway, first_arg, second_arg)
return whichway.call(first_arg, second_arg)
end

puts doStuff(method(:firstWay), nil, nil)


Rados³aw Bu³at

http://radarek.jogger.pl - mój blog

Rados³aw Bu³at wrote:

end

def secondWay(arg1, arg2)
return ‘Less filling.’
end

def doStuff(whichway, first_arg, second_arg)
return whichway.call(first_arg, second_arg)
end

puts doStuff(method(:firstWay), nil, nil

Ah, that’s the one I was looking for. Then you can do:

def doStuff(whichway, first_arg, second_arg)
whichway[first_arg, second_arg]
end

-Justin

On Jan 17, 8:12 pm, Justin C. [email protected] wrote:

So calling:
first_pointer = Proc.new do |foo, bar|

irb(main):006:1> end
=> “No! Tastes great!”

-Justin

Ah–that is nicer–thanks!

My momentary python envy (hmmm…) has now passed. :wink:

-Roy

On 18.01.2008 20:11, Justin C. wrote:

return ‘Tastes great.’
puts doStuff(method(:firstWay), nil, nil

Ah, that’s the one I was looking for. Then you can do:

def doStuff(whichway, first_arg, second_arg)
whichway[first_arg, second_arg]
end

Yeah, but do you really consider

doStuff(method(:firstWay), nil, nil)

more concise than

send(:firstWay, nil, nil)

?

Cheers

robert

On Jan 18, 2008 6:50 PM, [email protected] [email protected] wrote:

My momentary python envy (hmmm…) has now passed. :wink:
Well that is good, but for the wrong reason :frowning:

Our Rubyists where cheating on you as you had a first class object
called a function, and we were just using names.
Now there are no functions in Ruby so we cannot pass them on, we do
however have proc objects which would mimick what you have done in
Python
firstway =lambda{ |x| x+1}
otherway = lambda{ |x| x - 1}
def anyway away, anumber
away.call anumber
end

However the interesting and somewhat frustrating stuff starts with
methods which are first class objects in Ruby
class A
def first x; x+1 end
def second x; x-1 end
@f = instance_method :first
@s = instance_method :second
class << self
attr_reader :f, :s
end

def any method, number
    method.bind( self ).call number
end

end

p A.new.any(A.f, 41)
p A.new.any(A.s,43)

Things are however a little bit trickier when it comes to binding
instance_methods
look at this code
class B
def b; 42 end
end

p B.instance_method(:b).bind(Object.new).call # :(((
I still fail to see the need for this restriction
Now lots of trickery is needed if you want to do things like these

module M
def m; 42 end
end

p Object.new.send(:extend, M).m

If you are interested in this stuff you might have a look at the
implementation of Traits in Ruby
http://rubyforge.org/projects/ruby-traits/
HTH
Robert


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

“R” == Robert D. [email protected] writes:

R> I still fail to see the need for this restriction

Without this restriction you can write

Array.instance_method(:[]).bind(Hash.new).call(0)

and if you are not lucky ruby just crash.

Guy Decoux

On Jan 19, 2008 1:59 PM, ts [email protected] wrote:

“R” == Robert D. [email protected] writes:

R> I still fail to see the need for this restriction

Without this restriction you can write

Array.instance_method(:[]).bind(Hash.new).call(0)

and if you are not lucky ruby just crash.
Bad example of mine, what disturbs me is the following

not working:
Module::new{ def a; 42 end}.instance_method(:a).bind(Object.new).call

not working:
a=Modul::new{ def a; 42 end}.i_m :a
Object.module_eval{ define_method :x, a }

working;
module M
def m; 42 end
end

Object.module_eval{
include M
define_method :x, M.instance_method(:m)
}
p Object.new.x

and I thought we were all ducktypers :frowning:

Robert

Guy Decoux


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

Robert K. wrote:

def firstWay(arg1, arg2)

doStuff(method(:firstWay), nil, nil)

more concise than

send(:firstWay, nil, nil)

?

Cheers

robert

Clearly this is just a toy example to show options for how one might
call methods using variable names. If the doStuff method actually did
more stuff, then it would no longer be equivalent to send.

-Justin

Hi there,

You can skip the between and do this:

def add_one(n)
n+1
end
=> nil

[3,6,8].map &method(:add_one)
=> [4, 7, 9]

Personally, I like doing it this way. You could always try
monkey-patching
(ooh, buzzword) Symbol by adding Symbol#to_proc to do some method
resolution. Not sure how well it’ll work for you, but a guess.

Arlen

What bugs me about ruby is that I can’t do this
def add_one(n)
n + 1
end
[3,6,8].map(add_one)
=> [4,7,9]

I can’t even do it more explicitly, because map won’t take anything
other than a block:

irb(main):001:0> class A; def A.addone(x); x + 1; end; end
=> nil
irb(main):002:0> A.method(:addone)
=> #<Method: A.addone>
irb(main):003:0> [3,6,8].map{|x| x + 1}
=> [4, 7, 9]
irb(main):004:0> [3,6,8].map(A.method(:addone))
ArgumentError: wrong number of arguments (1 for 0)
from (irb):4:in map' from (irb):4 irb(main):005:0> [3,6,8].map(Proc.new{|x| A.method(:addone).call(x)}) ArgumentError: wrong number of arguments (1 for 0) from (irb):5:inmap’
from (irb):5
irb(main):006:0> [3,6,8].map Proc.new{|x| A.method(:addone).call(x)}
ArgumentError: wrong number of arguments (1 for 0)
from (irb):6:in `map’
from (irb):6

Any of these would be defeating the purpose of brevity anyway. The best
I can do is to make a block that calls the proc I want:

irb(main):007:0> [3,6,8].map{|x| A.method(:addone).call(x)}
=> [4, 7, 9]

or more simply:
irb(main):008:0> [3,6,8].map{|x| A.addone(x)}
=> [4, 7, 9]

this seems a waste of keystrokes, a cognitive indirection, and probably
even a few processor cycles. Is there a way?

Grem

Sorry, I missed your exact example. It works the same:

class A; def A.add_one(n); n + 1; end; end
=> nil

[3, 6, 8].map &A.method(:add_one)
=> [4, 7, 9]

Cheers,
Arlen.

On Sun, Feb 24, 2008 at 2:55 PM, Arlen C. [email protected] wrote:

=> [4, 7, 9]

Personally, I like doing it this way. You could always try monkey-patching
(ooh, buzzword) Symbol by adding Symbol#to_proc to do some method
resolution.
Oh you might do this even very often if you are writing Ruby1.8/1.9
agnostic libraries.
Cheers
Robert


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

Gregory M. wrote:

What bugs me about ruby is that I can’t do this
def add_one(n)
 n + 1
end
[3,6,8].map(add_one)
 => [4,7,9]

[3,6,8].map(&method(:add_one))
#=> [4, 7, 9]

And yeah, it’d be nice if there was a more concise syntax than that, but
that’s what we’ve currently got. Thinking about it, you could do
something
like this if you want a shorter syntax:

class Symbol
def -@()
method(self)
end
end
[3,6,8].map(&-:add_one)

But that’s kinda hackish.

HTH,
Sebastian

Another potentially easy question:

I’d like to pass a surround method into another, where the surround
method would take a block, and should yield to it, perhaps doing stuff
before and after. This is not a real call/cc surround, just something
simple. So:

def do_things(options={})
opts = {:surround => Proc.new{yield}}.merge(options)
opts[:surround] do
the actual task
end
end

except this doesn’t actually work with Procs, because opts[:surround] is
an undefined method, of course. It contains a method reference. Well,
rather, a procedure reference. In any case, I want to invoke it with a
block.

so I think
y = lambda{ yield }
y.call{ 3 }
perhaps, but no:
LocalJumpError: no block given

How shall I pass it a block?

Thanks,
Grem

Robert D. wrote:

On Sun, Feb 24, 2008 at 2:55 PM, Arlen C. [email protected] wrote:

=> [4, 7, 9]

Personally, I like doing it this way. You could always try monkey-patching
(ooh, buzzword) Symbol by adding Symbol#to_proc to do some method
resolution.
Oh you might do this even very often if you are writing Ruby1.8/1.9
agnostic libraries.
Cheers
Robert

Could you both elaborate? I’m not sure what it would mean to
“monkeypatch” this, or which differences with 1.9 will require it.
Thanks already: this is a bit C-like, but nicer than wrapping a block
around things.

Grem

“G” == Gregory M. [email protected] writes:

G> so I think
G> y = lambda{ yield }
G> y.call{ 3 }
G> perhaps, but no:
G> LocalJumpError: no block given

Wait for 1.9, it will be easy to do it. It’s marked actually as an
“known
bug” in 1.9

vgs% more bootstraptest/test_knownbug.rb

This test file concludes tests which point out known bugs.

So all tests will cause failure.

assert_equal ‘ok’, %q{
class C
define_method(:foo) {
if block_given?
:ok
else
:ng
end
}
end
C.new.foo {}
}, ‘[ruby-core:14813]’

[…]
vgs%

Guy Decoux

On Sun, Feb 24, 2008 at 3:05 PM, Gregory M.
[email protected] wrote:

Cheers
Robert

Could you both elaborate? I’m not sure what it would mean to
“monkeypatch” this, or which differences with 1.9 will require it.
Thanks already: this is a bit C-like, but nicer than wrapping a block
around things.

Symbol#to_proc exists in Ruby1.9 standard, if you want to have
programs run in Ruby1.8 too you will be forced to define your
Symbol#to_proc yourself.
But I guess that I am wrong that you have to do that often, that was
written with very little thinking done :(.

Robert

Grem

Posted via http://www.ruby-forum.com/.


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein