In Ruby, is there a way to curry function arguments out of the order in which they were originally declared?
Here's a really simple example to demonstrate what I mean:
# Example data, an array of arrays
list = [
[11, 12, 13, 14],
[21, 22, 23, 24],
[31, 32, 33, 34],
[41, 42, 43, 44]
]
# Declare a simple lambda
at = ->(arr, i) { arr[i] }
To return the first element of the first array, the second element of the
second array, etc. you can just use #with_index
:
# Supply the lambda with each array, and then each index
p list.map.with_index(&at)
# => [11, 22, 33, 44]
But this use case is somewhat contrived. A more realistic use for this &at
lambda would be to return, for example, the second element in each of the arrays.
It seems I would have to redeclare the lambda with swapped arguments, because the argument I want to curry is not in the first position:
# The same lambda, but with swapped argument positions
at = ->(i, arr) { arr[i] }
# Supply the lambda with the integer 1, and then each array
p list.map(&at.curry[1])
# => [12, 22, 32, 42]
Or by creating a proxy interface like the below:
at_swap = ->(i, arr) { at.call(arr, i) }
Is this correct? Is there no way to curry out of order? I feel like this would be useful to enable us to better reuse procs and methods, but perhaps I've overlooked something.
There are some similar questions on this site, but none have concrete answers or workarounds.
Ruby Reverse Currying: Is this possible?
Currently standard library of Ruby doesn't provide such option.
However you could easily implement a custom method which allows you to change the order of the arguments of Procs and lambdas. For example I'll present an imitation of Haskell's flip
function:
flip f
takes its (first) two arguments in the reverse order off
How would it look like in Ruby?
def flip
lambda do |function|
->(first, second, *tail) { function.call(second, first, *tail) }.curry
end
end
And now we can use this method to change the order of our lambdas.
concat = ->(x, y) { x + y }
concat.call("flip", "flop") # => "flipflop"
flipped_concat = flip.call(concat)
flipped_concat.call("flip", "flop") # => "flopflip"
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