views:

268

answers:

7

In Ruby, everything is supposed to be an object. But I have a big problem to get to the function object defined the usual way, like

def f
    "foo"
end

Unlike in Python, f is the function result, not the function itself. Therefore, f(), f, ObjectSpace.f are all "foo". Also f.methods returns just string method list.

How do I access the function object itself?

A: 

You can obtain an object that will correspond to that function at runtime, but as far as I know there's no equivalent to what Python allows you to do right there in the function/class definition.

Hopefully someone will prove me wrong, but I've never been able to pull it off.

Azeem.Butt
+1  A: 

In Ruby, what you are asking for does not make sense. (It makes sense in languages like Python, JavaScript, Java, and C#.)

In Ruby, you can tell a class what messages it should respond to and in what ways it should respond to those messages. The most common way to tell a class that is to define a method in the class. But again, that simply means: when someone sends the message f to this instance, here's the behavior that the instance should respond with. There is no function object.

You can do things like get a symbol (method_name = :foo) and get a closure (method = proc { obj.foo }).

Justice
if f is not an object, why is it in ObjectSpace then?
mykhal
Methods are objects. They're just not objects the runtime allows you to access WHILE they're still being defined. If you want to do that then stick to Python.
Azeem.Butt
Actually, there is a Method class that all methods are an instance of. When you do `obj.some_method` in Ruby, it sends the message to obj, which looks in it's method list for a matching method, and then calls that method. If it can't find the method, it calls `method_missing`.
dvyjones
+1  A: 

That isn't a function; it's a method of an object. If you want to get an object representing a method, you ask the owning object for it:

someobj.method :f

For example:

plus_one = 1.method :+
plus_one[9] # 10
Chuck
`def f; "foo"; end` is really not a function?
mykhal
Conceptually, no — it's a method. It's inherently bound to an owner in Ruby. The closest Ruby equivalent to true functions would be Procs.
Chuck
+2  A: 

The method method will give you a Method object

f = obj.method :f
puts f.call # or f[]

this f is bound to obj. You can also get an unbound method:

unbound_plus = Fixnum.instance_method('+')
plus_2 = unbound_plus.bind(2)

(There is also a unbind method)

Justin Love
so you'd say that my f is unbound method. it looks reasonable. however, the fact that my f als act as ObjectSpace methods (which otherwise return the objects) is confusing
mykhal
well, it still does not answer my question, how do I treat my f as Method instance
mykhal
@mykhal: Any method you define at the top level becomes an instance method of Object, so ObjectSpace (which is an object, just like everything else in the language) gets the method too. You shouldn't normally be able to call it from outside of ObjectSpace, though — methods defined that way default to being private.
Chuck
+2  A: 

well I have found the answer myself

obj = method :f

thank you all for the inspiration :)

mykhal
This gives you the method, but not a pure function. Creating a `Proc` object is maybe closer to what you want. A method always requires a receiver, since it is bound to an object, a proc(edure) is an object by itself and can thus just be called like a function.
Adrian
@Adrian it gives unbound method. try yourself: `ruby -e 'def f; "foo"; end; obj = method :f; puts obj.call'`
mykhal
+3  A: 

You simply use the method method. This will return the Method instance that matches with that method. Some examples:

>> def f
>>   "foo"
>> end
=> nil
>> f
=> "foo"
>> method(:f)
=> #<Method: Object#f>
>> method(:f).methods
=> [:==, :eql?, :hash, :clone, :call, :[], ...]
>> class SomeClass
>>   def f
>>     "bar"
>>   end
>> end
=> nil
>> obj = SomeClass.new
=> #<SomeClass:0x00000001ef3b30>
>> obj.method(:f)
=> #<Method: SomeClass#f>
>> obj.method(:f).methods
=> [:==, :eql?, :hash, :clone, :call, :[], ...]

Hope this helps.

dvyjones
Oh, and the Method object is a Proc-like object, so you doo `obj.method(:f).call()` or `obj.method(:f)[]` to call it. Or, on Ruby 1.9.1; `obj.method(:f).()`.
dvyjones
A: 

You should create a Proc object if you want a function object

f = Proc.new { |x| x * 2 }

you call the function using either

f.call(8) # => 16

or

f[8] # => 16

you may even use this function object in a map operation

[1, 2, 3, 4, 5].map(&f) # => [2, 4, 6, 8, 10]
Adrian