Say I have a generic Proc
, Lambda
or method
which takes an optional second argument:
pow = -> (base, exp: 2) { base**exp }
Now I want to curry this function, giving it an exp
of 3
.
cube = pow.curry.call(exp: 3)
There's an ambiguity here, arising from the keyword arguments and the new hash syntax, where Ruby interprets exp: 3
as a hash being passed as the first argument, base
. This results in the function immediately being invoked, rendering a NoMethodError
when #**
is sent to the hash.
Setting a default value for the first argument will similarly result in the function being immediately invoked when currying, and if I mark the first argument as required, without providing a default:
pow = -> (base:, exp: 2) { base**exp }
the interpreter will complain that I'm missing argument base
when I attempt to curry the Proc
.
How can I curry a function with the second argument?
You could build your own keyword-flavored curry method that collects keyword arguments until the required parameters are present. Something like:
def kw_curry(method)
-> (**kw_args) {
required = method.parameters.select { |type, _| type == :keyreq }
if required.all? { |_, name| kw_args.has_key?(name) }
method.call(**kw_args)
else
-> (**other_kw_args) { kw_curry(method)[**kw_args, **other_kw_args] }
end
}
end
def foo(a:, b:, c: nil)
{ a: a, b: b, c: c }
end
proc = kw_curry(method(:foo))
proc[a: 1] #=> #<Proc:0x007f9a1c0891f8 (lambda)>
proc[b: 1] #=> #<Proc:0x007f9a1c088f28 (lambda)>
proc[a: 1, b: 2] #=> {:a=>1, :b=>2, :c=>nil}
proc[b: 2][a: 1] #=> {:a=>1, :b=>2, :c=>nil}
proc[a: 1, c: 3][b: 2] #=> {:a=>1, :b=>2, :c=>3}
The example above is limited to keyword arguments only, but you can certainly extend it to support both, keyword arguments and positional arguments.
I don't think you can do it with Proc.curry
, but there is always the longhand way
cube = -> (base) {pow.(base, exp: 3)}
You could also create a factory function
pow_factory = -> (exp) {-> (base) {pow.(base, exp: exp)}}
cube = pow_factory.(3)
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