JavaScript happy times fun land
// make a method
var happy = function(a, b, c) {
console.log(a, b, c);
};
// store method to variable
var b = happy;
// bind a context and some arguments
b.bind(happy, 1, 2, 3);
// call the method without additional arguments
b();
Output. Yay!
1 2 3
In Ruby
# make a method
def sad a, b, c
puts a, b, c
end
# store method to variable
b = method(:sad)
# i need some way to bind args now
# (this line is an example of what i need)
b.bind(1, 2, 3)
# call the method without passing additional args
b.call
Desired output
1, 2, 3
For what it's worth, I know JavaScript can change the context of the binding with the first argument passed to .bind
. In Ruby, I'd be 99% happy even if I couldn't change the context. I primarily need to simply bind parameters to the method.
Question
Is there a way to bind parameters to an instance of a Ruby Method
such that when I call method.call
without additional parameters, the bound parameters are still passed to the method?
Goal
This is a common JavaScript idiom and I think it would be useful in any language. The goal is to pass a method M
to a receiver R
where R does not need (or have) intrinsic knowledge of which (or how many) parameters to send to M when R executes the method.
A JavaScript demonstration of how this might be useful
/* this is our receiver "R" */
var idiot = function(fn) {
console.log("yes, master;", fn());
};
/* here's a couple method "M" examples */
var calculateSomethingDifficult = function(a, b) {
return "the sum is " + (a + b);
};
var applyJam = function() {
return "adding jam to " + this.name;
};
var Item = function Item(name) {
this.name = name;
};
/* here's how we might use it */
idiot(calculateSomethingDifficult.bind(null, 1, 1));
// => yes master; the sum is 2
idiot(applyJam.bind(new Item("toast")));
// => yes master; adding jam to toast
prototype. bind() The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
JavaScript Function bind() With the bind() method, an object can borrow a method from another object. The example below creates 2 objects (person and member).
The full syntax of bind : let bound = func. bind(context, [arg1], [arg2], ...); It allows to bind context as this and starting arguments of the function.
We use the Bind() method to call a function with the this value, this keyword refers to the same object which is currently selected . In other words, bind() method allows us to easily set which object will be bound by the this keyword when a function or method is invoked.
Normally, rebinding methods isn't something you do in Ruby. Instead, you use blocks:
# This is our receiver "R"
def idiot(&block)
puts("yes, master; #{block.call}")
end
# Here's a couple method "M" examples
def calculateSomethingDifficult(a, b)
return "the sum is #{a + b}"
end
def applyJam(object)
return "adding jam to " + object.name
end
class Item
attr_reader :name
def initialize(name)
@name = name
end
end
# Here's how we might use it
idiot do
calculateSomethingDifficult(1, 1)
end
#=> yes master; the sum is 2
# You *can* change calling context too (see instance_exec), but I'd
# discourage it. It's probably better to just pass the object as a
# parameter.
idiot do
applyJam(Item.new("toast"))
end
#=> yes master; adding jam to toast
If you really want to "bind" methods like you do in JavaScript it's definitely possible though:
class Method
def bind *args
Proc.new do |*more|
self.call *(args + more)
end
end
end
That should make your example work almost as you originally described:
# make a method
def sad a, b, c
puts a, b, c
end
# store method to variable
b = method(:sad)
# Get a "bound" version of the method
b = b.bind(1, 2, 3)
# call the method without passing additional args
b.call
If you need it exact, you can probably define Object#bindable_method
to return some BindableMethod
class that does what you want. For most cases though I think the above should work for you.
Proc#curry
in Ruby is similar to bind
in JavaScript.
def happy(a, b, c, d = 100)
puts a, b, c, d
end
proc = method(:happy).to_proc.curry # proc is now a curried Proc
b = proc.call(1,2) # b is a curried Proc with 1 and 2 bound as the first arguments
b.call(3) # Call the proc, providing the 3rd argument
You can't exactly duplicate your example code because when the curried proc is called with the necessary arguments, it returns the result of the proc --- in other words, you cannot bind ALL of the arguments and then call the proc later --- you have to leave at least one argument unbound.
This isn't necessarily a better option than the code provided by Ajedi32, but I think it's worth mentioning because it's built-in to Ruby.
See the docs here: http://ruby-doc.org/core-2.2.0/Proc.html#method-i-curry
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With