Pass arbitrary number of lambdas (or procs) in Ruby

Viewed 71

I am getting my head around the functional model in Ruby and ran into a problem. I am able to successfully pass any given number of arguments to an arbitrary function as follows:

add = ->(x, y) { return x + y }
mul = ->(x, y) { return x * y }

def call_binop(a, b, &func)
  return func.call(a, b)
end

res = call_binop(2, 3, &add)
print("#{res}\n")   #5

res = call_binop(3, 4, &mul)
print("#{res}\n")   #12

However, I am not able to pass an arbitrary number of functions:

dbl = ->(x) { return 2 * x }
sqr = ->(x) { return x * x }

def call_funccomp(a, &func1, &func2)
  return func2.call(func1.call(a))
end

res = call_funccomp(3, &dbl, &sqr)
print("#{res}\n")   #Expect 36 but compiler error

The compiler error is syntax error, unexpected ',', expecting ')'

I have already added both lambdas and procs to an array and then executed elements of the array, so I know I can get around this by passing such an array as an argument, but for simple cases this seems to be a contortion for something (I hope) is legal in the language. Does Ruby actually limit the number or lambdas one can pass in the argument list? It seems to have a reasonably modern, flexible functional model (the notation is a little weird) where things can just execute via a call method.

1 Answers

Does Ruby actually limit the number or lambdas one can pass in the argument list?

No, you can pass as many procs / lambdas as you like. You just cannot pass them as block arguments.

Prepending the proc with & triggers Ruby's proc to block conversion, i.e. your proc becomes a block argument. And Ruby only allows at most one block argument.

Attempting to call call_funccomp(3, &dbl, &sqr) is equivalent to passing two blocks:

call_funccomp(3) { 2 * x } { x * x }

something that Ruby doesn't allow.

The fix is to omit &, i.e. to pass the procs / lambdas as positional arguments:

dbl = ->(x) { 2 * x }
sqr = ->(x) { x * x }

def call_funccomp(a, func1, func2)
  func2.call(func1.call(a))
end

res = call_funccomp(3, dbl, sqr)
print("#{res}\n") 

There's also Proc#>> which combines two procs:

def call_funccomp(a, func1, func2)
  (func1 >> func2).call(a)
end
Related