Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Currying out of order in Ruby

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?

Ruby rcurry. How I can implement proc “right” currying?

Currying a proc with keyword arguments

like image 869
Nossidge Avatar asked Nov 08 '22 06:11

Nossidge


1 Answers

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 of f

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"
like image 142
Walerian Sobczak Avatar answered Nov 12 '22 09:11

Walerian Sobczak