How to Reference a Function in Ruby

How do I reference a function in Ruby?

Ruby doesn't have functions. It only has methods (which aren't first-class) and Procs which are first-class, but are not associated with any object.

So, this is a method:

def foo(bar) puts bar end

foo('Hello')
# Hello

Oh, and, yes, this is a real method, not a top-level function or procedure or something. Methods defined at the top-level end up as private(!) instance methods in the Object class:

Object.private_instance_methods(false) # => [:foo]

This is a Proc:

foo = -> bar { puts bar }

foo.('Hello')
# Hello

Notice that Procs are called differently from methods:

foo('Hello')  # method
foo.('Hello') # Proc

The foo.(bar) syntax is just syntactic sugar for foo.call(bar) (which for Procs and Methods is also aliased to foo[bar]). Implementing a call method on your object and then calling it with .() is the closest thing you will get to Python's __call__ables.

Note that an important distinction between Ruby Procs and Python lambdas is that there are no restrictions: in Python, a lambda can only contain a single statement, but Ruby doesn't have the distinction between statements and expressions (everything is an expression), and so this limitation simply doesn't exist, therefore in a lot of cases where you need to pass a named function as an argument in Python because you cannot express the logic in a single statement, you would in Ruby simply pass a Proc or a block instead, so that the problem of the ugly syntax for referencing methods doesn't even arise.

You can wrap a method in a Method object (which essentially duck-types Proc) by calling the Object#method method on an object (which will give you a Method whose self is bound to that particular object):

foo_bound = method(:foo)

foo_bound.('Hello')
# Hello

You can also use one of the methods in the Module#instance_method family to get an UnboundMethod from a module (or class, obviously, since a class is-a module), which you can then UnboundMethod#bind to a particular object and call. (I think Python has the same concepts, albeit with a different implementation: an unbound method simply takes the self argument explicitly, just like the way it is declared.)

foo_unbound = Object.instance_method(:foo) # this is an UnboundMethod

foo_unbound.('Hello')
# NoMethodError: undefined method `call' for #<UnboundMethod: Object#foo>

foo_rebound = foo_unbound.bind(self) # this is a Method

foo_rebound.('Hello')
# Hello

Note that you can only bind an UnboundMethod to an object which is an instance of the module you took the method from. You cannot use UnboundMethods to "transplant" behavior between unrelated modules:

bar = module Foo; def bar; puts 'Bye' end; self end.instance_method(:bar)
module Foo; def bar; puts 'Hello' end end

obj = Object.new
bar.bind(obj)
# TypeError: bind argument must be an instance of Foo

obj.extend(Foo)
bar.bind(obj).()
# Bye
obj.bar
# Hello

Note, however, that both the Method and the UnboundMethod are wrappers around the method, not the method itself. Methods are not objects in Ruby. (Contrary to what I have written in other answers, BTW. I really need to go back and fix those.) You can wrap them in objects, but they aren't objects, and you can see that because you essentially get all the same problems you always get with wrappers: identity and state. If you call method multiple times for the same method, you will get a different Method object every time. If you try to store some state on that Method object (such as Python-style __doc__strings, for example), that state will be private to that particular instance, and if you try to retrieve your docstring again via method, you will find that it is gone.

There is also syntactic sugar in the form of the method reference operator .::

bound_method = obj.:foo

Which is identical to

bound_method = obj.method(:foo)

How can I get a reference to a method?

You want Object#method:

---------------------------------------------------------- Object#method
obj.method(sym) => method
------------------------------------------------------------------------
Looks up the named method as a receiver in obj, returning a Method
object (or raising NameError). The Method object acts as a closure
in obj's object instance, so instance variables and the value of
self remain available.

class Demo
def initialize(n)
@iv = n
end
def hello()
"Hello, @iv = #{@iv}"
end
end

k = Demo.new(99)
m = k.method(:hello)
m.call #=> "Hello, @iv = 99"

l = Demo.new('Fred')
m = l.method("hello")
m.call #=> "Hello, @iv = Fred"

Now your code becomes:

private
def setup_map
@map = {
'a' => method(:a),
'b' => method(:b),
'c' => method(:c)
}
# or, more succinctly
# @map = Hash.new { |_map,name| _map[name] = method(name.to_sym) }
end

public
def call(arg)
@map["a"][arg] if arg > 10
@map["b"][arg] if arg > 20
@map["c"][arg] if arg > 30
end

How to reference a method in another Ruby code file?

If you are using Ruby 1.9 or later, this is the simplest way to do it:

require_relative 'somelogic'

If you want your code to work in 1.9 and older versions of Ruby, you should do this instead:

require File.join File.dirname(__FILE__), 'somelogic'

Whichever line you choose, you should put it at the top of your ruby file. Then any classes, modules, or global variables defined in somelogic.rb will be available to your program.

How to pass by reference in Ruby?

Ruby is strictly pass-by-value, which means references in the caller's scope are immutable. Obviously they are mutable within the scope, since you can assign to them after all, but they don't mutate the caller's scope.

a = 'foo'

def bar(b)
b = 'bar'
end

bar(a)

a
# => 'foo'

Note, however, that the object can be mutated even if the reference cannot:

a = 'foo'

def bar(b)
b << 'bar' # the only change: mutate the object instead of the reference
b = 'baz'
end

bar(a)

a
# => 'foobar'

If the object you get passed is immutable, too, then there is nothing you can do. The only possibilities for mutation in Ruby are mutating the reference (by assignment) or asking an object to mutate itself (by calling methods on it).

You can return an updated value from your method and have the caller assign that to the reference:

a = :foo

def bar(b)
:"#{a}bar"
end

c = bar(a)

c
# => :foobar

Or you can wrap the immutable object in a mutable one and mutate that mutable wrapper:

a = [:foo]

def bar(b)
b[0] = :bar
end

bar(a)

a
# => [:bar]

[This is really the same thing as the second example above.]

But if you can't change anything, then you are stuck.

Ruby string pass by reference function parameter

I understand ruby does pass by reference for function parameters

Ruby is strictly pass-by-value, always. There is no pass-by-reference in Ruby, ever.

This is simply out of curiosity -- any explanations would be appreciated

The simple explanation for why your code snippet doesn't show the result you would expect for pass-by-reference is that Ruby isn't pass-by-reference. It is pass-by-value, and your code snippet proves that.

Here is a small snippet that demonstrates that Ruby is, in fact, pass-by-value and not pass-by-reference:

#!/usr/bin/env ruby

def is_ruby_pass_by_value?(foo)
foo << <<~HERE
More precisely, it is call-by-object-sharing!
Call-by-object-sharing is a special case of pass-by-value,
where the value is always an immutable pointer to a (potentially mutable) value.
HERE
foo = 'No, Ruby is pass-by-reference.'
return
end

bar = ['Yes, of course, Ruby *is* pass-by-value!']

is_ruby_pass_by_value?(bar)

puts bar
# Yes, of course, Ruby *is* pass-by-value!,
# More precisely, it is call-by-object-sharing!
# Call-by-object-sharing is a special case of pass-by-value,
# where the value is always an immutable pointer to a (potentially mutable) value.

Ruby does however allow mutation of objects, it is not a purely functional language like Haskell or Clean.

ruby module as collection of methods

If you define module methods within the context of the module itself, they can be called without import:

module FilterOptions
module Select
def self.some_select
return "some information"
end
end
end

puts FilterOptions::Select.some_select
# => "some information"

It is also possible to import one module, and not import the next, refer to it by name instead:

include FilterOptions
puts Select.some_select
# => "some information"

Is Ruby pass by reference or by value?

In traditional terminology, Ruby is strictly pass-by-value. But that's not really what you're asking here.

Ruby doesn't have any concept of a pure, non-reference value, so you certainly can't pass one to a method. Variables are always references to objects. In order to get an object that won't change out from under you, you need to dup or clone the object you're passed, thus giving an object that nobody else has a reference to. (Even this isn't bulletproof, though — both of the standard cloning methods do a shallow copy, so the instance variables of the clone still point to the same objects that the originals did. If the objects referenced by the ivars mutate, that will still show up in the copy, since it's referencing the same objects.)

What's the equivalent to method reference in Ruby

You need to grab the method by using method with the method’s name as an argument. This will return you an instance of type Method, which can be called with call().

class MyClass
def myMethod
"Hi"
end
end

x = MyClass.new
m = x.method(:myMethod)
# => #<Method: MyClass#myMethod>

puts m.call
# You can also do m[] instead of m.call()

Note that any arguments would need to be added to the call method.


In many practical cases, however, there is no need to have the method itself saved to a variable in Ruby; if you just want to dynamically call a method (i.e. send a message to an object) and there is no need to save the method, you could also use the send (or __send__ method in case of name clashes).

x = MyClass.new
puts x.send :myMethod # also possible with a string: m.send "myMethod"
# "Hi"

Any arguments should follow the method name:

puts x.send(:myMethod, arg1, arg2)

To use it like this is probably more Ruby-like, as the concept of Method classes is not as prominent as it is in Python. In Python, you can always think of a two step mechanism when doing something like a_string.split(); first you grab the method with a_string.split and then you call it (either implicitly with () or explicitly with __call__()). So, cutting that two-step mechanism is rather natural to do.

Ruby is more based on message passing and to actually get a method class in Ruby, you’ll have to do some more work, because in some way, the method object will have to be constructed for you at that point. So, unless you really need some Methods object in Ruby, you should rather stick to the message passing abstraction and simply use send.



Related Topics



Leave a reply



Submit