2

Ruby 1.9's built in support of currying supports two ways to deal with a proc taking an arbitrary number of arguments:

my_proc = proc {|*x| x.max }

1) curry without arguments: my_proc.curry. You pass the comma-separated arguments to the curried proc like you would to the normal proc. This does not achieve proper currying if the number of arguments is arbitrary (it is useful if some of the arguments are not splat)

2) curry with arguments: my_proc.curry(n) This way, the currying gets applied as if the proc would take n arguments. For example:

my_proc.curry(3).call(2).call(5).call(1) #=> 5

So, how would you achieve currying with an arbitrary number of arguments? That means, if n is not given?

One way that comes into my mind is to collect the arguments via proxying call and then resolve the proc via method_missing (if any method other than call is used / call is used without arguments, call the proc with the collected arguments), but I'm still looking for other ways to achieve it.

Update

As Andy H stated, the problem is when to stop currying. For my purposes, it would be ok if the currying stops / the proc evaluates when either any method besides call is invoked or call is invoked without arguments.

Beat Richartz
  • 9,474
  • 1
  • 33
  • 50

3 Answers3

7

The built-in curry method won't work for you. The reason is that it produces a proc that evaluates as soon as it has enough arguments to do so. Quoting the doc you linked to:

A curried proc receives some arguments. If a sufficient number of arguments are supplied, it passes the supplied arguments to the original proc and returns the result.

The key point to realise here is that zero is a "sufficient number of arguments" for a splat parameter.

f = ->(x, y, z, *args){ [x, y, z, args] } 
g = f.curry    # => a "curryable" proc
h = g.call(1)  # => another curryable proc
i = h.call(2)  # => another curryable proc
j = i.call(3)  # => [1, 2, 3, []]

As you show, these "curryable" procs are can be passed their arguments one-at-a-time, each time returning a new curryable proc until sufficient arguments have been passed, at which point it evaluates. This is also why they couldn't support arbitrary-length argument lists - how would it know when to stop currying and just evaluate?

If you wanted a different way of currying that allowed arbitrary numbers of arguments, you could define your own curry method:

def my_curry(f, *curried_args)
  ->(*args) { f.call(*curried_args, *args) }
end

This is a fairly simplistic implementation, but might serve your purposes. The key differences to the built-in method are that it always returns a new proc, even if sufficient arguments have been passed, and it doesn't support the one-at-a-time "curry chaining".

f = ->(x, y, z, *args) { [x, y, z, args] }

g = my_curry(f, 1)    # => a proc
g.call(2, 3)          # => [1, 2, 3, []] 
g.call(2, 3, 4, 5)    # => [1, 2, 3, [4, 5]]
g.call(2)             # => ArgumentError: wrong number of arguments (2 for 3)

h = my_curry(g, 2, 3) # => a proc
h.call                # => [1, 2, 3, []]
h.call(4, 5)          # => [1, 2, 3, [4, 5]]
Andrew Haines
  • 6,574
  • 21
  • 34
  • Thanks for your answer. As you said, the problem with currying and arbitrary arguments is when to stop currying and evaluate. For my purposes, it would be ok if the currying stops / the proc evaluates when either 1) any method besides `call` is invoked, or 2) `call` is invoked without arguments. I'm looking for an elegant way to do that. But +10 from me for the detailed answer. – Beat Richartz Feb 08 '13 at 10:24
2

I hate to be humpfhoxious, but how about forget curry and just write

my_proc = proc { |*x| x.max }
l = -> *x { my_proc.( 4, 6, 12, 1, *x ) }
l.call + 1 #=> 13

You know, Matz himself said that curry is "just a toy for functional kids". Its implementation is not special, it is not faster or different from what you see above. Curry is not that useful in Ruby...

And for those who do not believe what I say, here is the exact quote and link:

Easter Egg for Functional Programming Kids

Boris Stitnicky
  • 12,444
  • 5
  • 57
  • 74
  • I'd like to see that quote in the internet somewhere not [stated by you](http://stackoverflow.com/questions/12290920/is-it-possible-to-do-functional-programming-in-a-language-without-functions). If you do not see any sense in currying, just leave it alone. Your answer is not in any way providing something helpful. – Beat Richartz Feb 08 '13 at 16:44
  • Edited with link to the original discussion. Obviously, the BDFL does not think that with `curry`, he is providing anything extra helpful either. – Boris Stitnicky Feb 08 '13 at 23:49
0

I realized a solution using a subclass of Basic Object as a wrapper to do currying:

class CurryWrap < BasicObject

  def initialize proc
    @proc = proc
    @args = []
    return self
  end

  def call *args
    if args.size == 1
      @args << args.first
      return self
    elsif args.empty?
      return @proc.call(*@args)
    else
      @proc.call(*@args).call(*args)
    end
  end

  def == other
    @proc.call(*@args) == other
  end

  def method_missing m, *args, &block
    @proc.call(*@args).send(m, *args, &block)
  end

end

With this, one can basically do the following:

my_proc = proc { |*x| x.max }

CurryWrap.new(my_proc).call(4).call(6).call(12).call(1) + 1 #=> 13

Anything besides call with an argument will resolve the proc.

I don't know if this is the most elegant way to do this though, so I'll leave the answer unaccepted for now.

Beat Richartz
  • 9,474
  • 1
  • 33
  • 50
  • So here you basically treat any message other than #call as a signal to stop currying. That leaves me wondering – wouldn't it be better to use an explicit signal for that? – Boris Stitnicky Feb 08 '13 at 16:21
  • @BorisStitnicky Depends on the case in my opinion. If you can apply anything else to resolve the proc, you also will be able to apply anything without currying, so the sense of currying goes away. So the thing is for the proc to resolve itself once it knows that there are no more arguments coming its way. – Beat Richartz Feb 08 '13 at 16:34