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?

mykhal
  • 19,175
  • 11
  • 72
  • 80
  • This is one of (many) things that drives me berserk in ruby. You can't refer to a function by name without invoking it. I have arguments about this with my cow orkers. – Jonathan Feinberg Dec 19 '09 at 16:55
  • I think that Ruby has its own way and some principles used in other languages simply do not apply (in this case I'd probably define the function with Proc, or use code block), but I still wonder if there is a positive answer to my question – mykhal Dec 19 '09 at 17:02
  • 1
    @Jonathan: You can refer to a method's name without calling it. A method's name is a symbol. So the name of this method is `:f`. Ruby is pretty famous for its metaprogramming capabilities, and method names are used all the time there. – Chuck Dec 20 '09 at 07:48
  • @Chuck Symbols aren't coupled with functions *at all*, they are just lightweight string objects (they only have a small subset of the methods) that are used instead of constants. Wherever you can pass a symbol as a method name, you can pass a string instead if you want. – sarahhodne Dec 20 '09 at 12:11
  • @dvyjones: I did not say they are coupled with methods. I said they can be used to refer to a method's name, in response to Jonathan's claim that "you can't refer to a method by name." In fact, it would be impossible for a method's name to be coupled with it, since many methods can share the same name. – Chuck Dec 20 '09 at 19:12

7 Answers7

10

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.

sarahhodne
  • 9,796
  • 3
  • 39
  • 44
  • 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).()`. – sarahhodne Dec 28 '09 at 18:06
3

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
  • 4,397
  • 25
  • 36
  • 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 Dec 19 '09 at 17:19
  • well, it still does not answer my question, how do I treat my f as Method instance – mykhal Dec 19 '09 at 17:26
  • @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 Dec 20 '09 at 07:50
3

well I have found the answer myself

obj = method :f

thank you all for the inspiration :)

mykhal
  • 19,175
  • 11
  • 72
  • 80
  • 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. – akuhn Dec 20 '09 at 11:48
  • @Adrian it gives unbound method. try yourself: `ruby -e 'def f; "foo"; end; obj = method :f; puts obj.call'` – mykhal Dec 22 '09 at 07:06
1

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 }).

yfeldblum
  • 65,165
  • 12
  • 129
  • 169
  • if f is not an object, why is it in ObjectSpace then? – mykhal Dec 19 '09 at 17:10
  • 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 Dec 19 '09 at 17:24
  • 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`. – sarahhodne Dec 19 '09 at 18:03
1

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
  • 234,037
  • 30
  • 302
  • 389
  • `def f; "foo"; end` is really not a function? – mykhal Dec 19 '09 at 17:11
  • 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 Dec 19 '09 at 19:24
0

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
  • 5,855
  • 1
  • 26
  • 22
0

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]
akuhn
  • 27,477
  • 2
  • 76
  • 91