How to Implement Curry(Partial Function) in Ruby

how to implement curry(partial function) in ruby

So here's how to do currying with blocks, rather than methods:

def curry(&block) 
arity = (block.arity >= 0) ? block.arity : -(block.arity + 1)
# return an immediate value if the block has one
return block[] if arity == 0

# otherwise, curry it argument by argument
args = []
innermost = lambda do |last,*extra|
args[arity-1] = last
block[*(args+extra)]
end
(0...(arity-1)).to_a.reverse.inject(innermost) do |inner,i|
lambda do |arg_i,*extra|
args[i] = arg_i
# pass extra arguments on to inner calls
if extra.empty?
inner
else
inner[*extra]
end
end
end
end

And it works fairly well in practice. Arguments can be curried or not, and
extra arguments are collected as usual:

irb> (curry { |x,y| x + y })[1,2]
#=> 3
irb> (curry { |x,y| x + y })[1][2]
#=> 3
irb> (curry { |x,*ys| ys << x })[1]
#=> [1]
irb> (curry { |x,*ys| ys << x })[1,2,3]
#=> [2, 3, 1]
irb> (curry { |x,y,*zs| zs << (x+y) })[1,2]
#=> [3]
irb> (curry { |x,y,*zs| zs << (x+y) })[1,2,4]
#=> [4, 3]
irb> (curry { |x,y,*zs| zs << (x+y) })[1][2]
#=> [3]
irb> (curry { |x,y,*zs| zs << (x+y) })[1][2,4]
#=> [4, 3]
irb> (curry { |a,b,c,d,e| a+b+c+d+e })[1,2,3,4,5]
#=> 15
irb> (curry { |a,b,c,d,e| a+b+c+d+e })[1][2][3][4][5]
#=> 15
irb> (curry { |a,b,c,d,e| a+b+c+d+e })[1,2][3][4][5]
#=> 15
irb> (curry { |a,b,c,d,e| a+b+c+d+e })[1][2,3,4][5]
#=> 15

I made the design decision to have no-arg blocks return an immediate value on currying:

irb> curry { 3 }
#=> 3
irb> curry { |*xs| xs }
#=> []

This is necessary to avoid having to end currying with a [] every time (and is fairly Haskell-like).

Ruby: provide an argument while turning proc to a block

Regarding your comment:

Strange, but it swaps arguments during the performance

Actually, the argument order is preserved.

curry returns a new proc that effectively collects arguments until there are enough arguments to invoke the original method / proc (based on its arity). This is achieved by returning intermediate procs:

def foo(a, b, c)
{ a: a, b: b, c: c }
end

curried_proc = foo.curry #=> #<Proc:0x007fd09b84e018 (lambda)>
curried_proc[1] #=> #<Proc:0x007fd09b83e320 (lambda)>
curried_proc[1][2] #=> #<Proc:0x007fd09b82cfd0 (lambda)>
curried_proc[1][2][3] #=> {:a=>1, :b=>2, :c=>3}

You can pass any number of arguments at once to a curried proc:

curried_proc[1][2][3]     #=> {:a=>1, :b=>2, :c=>3}
curried_proc[1, 2][3] #=> {:a=>1, :b=>2, :c=>3}
curried_proc[1][2, 3] #=> {:a=>1, :b=>2, :c=>3}
curried_proc[1, 2, 3] #=> {:a=>1, :b=>2, :c=>3}

Empty arguments are ignored:

curried_proc[1][][2][][3] #=> {:a=>1, :b=>2, :c=>3}

However, you obviously can't alter the argument order.


An alternative to currying is partial application which returns a new proc with lower arity by fixing one or more arguments. Unlike curry, there's no built-in method for partial application, but you can easily write your own:

my_proc = -> (arg, num) { arg * num }

def fix_first(proc, arg)
-> (*args) { proc[arg, *args] }
end

fixed_proc = fix_first(my_proc, 'foo') #=> #<Proc:0x007fa31c2070d0 (lambda)>
fixed_proc[2] #=> "foofoo"
fixed_proc[3] #=> "foofoofoo"

[2, 3].map(&fixed_proc) #=> ["foofoo", "foofoofoo"]

Or fixing the last argument:

def fix_last(proc, arg)
-> (*args) { proc[*args, arg] }
end

fixed_proc = fix_last(my_proc, 2) #=> #<Proc:0x007fa31c2070d0 (lambda)>
fixed_proc['foo'] #=> "foofoo"
fixed_proc['bar'] #=> "barbar"

['foo', 'bar'].map(&fixed_proc) #=> ["foofoo", "barbar"]

Of course, you are not limited to fixing single arguments. You could for example return a proc that takes an array and converts it to an argument list:

def splat_args(proc)
-> (array) { proc[*array] }
end

splatting_proc = splat_args(my_proc)
[['foo', 1], ['bar', 2], ['baz', 3]].map(&splatting_proc)
#=> ["foo", "barbar", "bazbazbaz"]

Haskell, is it possible to create a curry function that can curry any number of tuple elements

One of the ways to implement such function is to use GHC.Generics. With this approach we don't even need to pass a number of parameters (or tuple size).
This works because there is an instance of Generic defined for tuples which effectively converts a tuple into tree structure (of type Rep a) which we can then traverse from right to left (using continuation passing style here) generating curried function along the way and packing the values of those parameters into identical Rep a structure which then converted into tuple with to function and passed to the original un-curried function-parameter. This code uses only type-level tree of parameters (from function is not used) since we produce the tuple rather then receiving it.
The only limitation of this approach is that Generic is defined only for up to eight-element tuple.

{-# LANGUAGE TypeOperators, MultiParamTypeClasses,
FlexibleInstances, UndecidableInstances,
TypeFamilies, ScopedTypeVariables #-}

import GHC.Generics

-- | class for `curryN` function
class CurryN t r where
type CurriedN t r :: *
curryN :: (t -> r) -> CurriedN t r

-- | Implementation of curryN which uses GHC.Generics
instance (Generic t, GCurryN (Rep t) r) => CurryN t r where
type CurriedN t r = GCurriedN (Rep t) r
curryN f = gcurryN (f . to)

-- | Auxiliary class for generic implementation of `curryN`
-- Generic representation of a tuple is a tree of its elements
-- wrapped into tuple constructor representation
-- We need to fold this tree constructing a curried function
-- with parameters corresponding to every elements of the tuple
class GCurryN f r where
type GCurriedN f r :: *
gcurryN :: (f p -> r) -> GCurriedN f r

-- | This matches tuple definition
-- Here we extract tree of tuple parameters and use other instances to "fold" it into function
instance (GCurryN f r) => GCurryN (D1 e1 (C1 e2 f)) r where
type GCurriedN (D1 e1 (C1 e2 f)) r = GCurriedN f r
gcurryN c = gcurryN (\t -> c (M1 (M1 t)))

-- | A node of the tree (combines at least two parameters of the tuple)
instance (GCurryN b r, GCurryN a (GCurriedN b r)) => GCurryN (a :*: b) r where
type GCurriedN (a :*: b) r = GCurriedN a (GCurriedN b r)
gcurryN c = gcurryN (\a -> gcurryN (\b -> c (a :*: b)))

-- | A leaf of the tree (a single tuple parameter)
instance GCurryN (S1 NoSelector (Rec0 a)) r where
type GCurriedN (S1 NoSelector (Rec0 a)) r = a -> r
gcurryN c = \a -> c $ M1 (K1 a)

-- Examples of usage
t2 = curryN (uncurry (&&))

t3 = curryN (\(a,b,c) -> a + b + c)

t4 = curryN (\(a,b,c,d) -> ((a , b) , (c , d)))

tf = curryN (\(f,a,xs) -> foldr f a xs)

t5 = curryN (\(a,b,c,d,e) -> (a ++ b , c - d, not e))

t7 = curryN (\(a1,a2,a3,a4,a5,a6,a7) -> a7)

What is 'Currying'?

Currying is when you break down a function that takes multiple arguments into a series of functions that each take only one argument. Here's an example in JavaScript:

function add (a, b) {
return a + b;
}

add(3, 4); // returns 7

This is a function that takes two arguments, a and b, and returns their sum. We will now curry this function:

function add (a) {
return function (b) {
return a + b;
}
}

This is a function that takes one argument, a, and returns a function that takes another argument, b, and that function returns their sum.

add(3)(4); // returns 7

var add3 = add(3); // returns a function

add3(4); // returns 7
  • The first statement returns 7, like the add(3, 4) statement.
  • The second statement defines a new function called add3 that will
    add 3 to its argument. (This is what some may call a closure.)
  • The third statement uses the add3 operation to add 3 to 4, again
    producing 7 as a result.

Can we use keyword arguments and curry until all arguments are received?

I came with a small script for my personal use.

PS: If anyone knows a library that can do something similar, please let me know.

import inspect

def curry(function):
original_func = function
original_args = inspect.signature(function).parameters

def wrapper(**kwargs):
call = True
current_args = {}
for each in original_args:
if kwargs.get(each):
current_args[each] = kwargs.get(each)
else:
call = False
if call:
original_func(**current_args)
else:
def partial(**nargs):
current_args.update(nargs)
return wrapper(**current_args)
return partial

return wrapper

@curry
def foo(a=None, b=None, c=None):
print(a, b, c)

# first partial
bar_with_a = bar(a=1)
# next partial
bar_with_a_and_b = bar_with_a(b=1)
# next partial
bar_with_a_and_b = bar_with_a_and_b(b=2)
# next partial
bar_with_a_and_b = bar_with_a_and_b(a=2)
# call
bar_with_a_and_b(c=2)


Related Topics



Leave a reply



Submit