[1.9] Proc#curry

25 views
Skip to first unread message

ts

unread,
Feb 14, 2008, 12:58:02 PM2/14/08
to ruby...@ruby-lang.org

Do I expect too much from Proc#curry ?

vgs% ./ruby -ve 'a = lambda {|x, y, &b| p b }; b = a.curry[1]; b.call(2) {}; a.call(1, 2) {}'
ruby 1.9.0 (2008-02-15 revision 15477) [i686-linux]
nil
#<Proc:0xb7d5d27c@-e:1>
vgs%


ruby don't see the block

Guy Decoux


David Flanagan

unread,
Feb 15, 2008, 12:24:33 AM2/15/08
to ruby...@ruby-lang.org

I can't comment on whether or not Guy has found a bug.

I'd like to ask, however, what the rationale is for adding this fairly
major new piece of API after 1.9.0. I could see this going into the 2.0
development branch, when it opens, but do we need it in 1.9? (I ask
this, obviously, as someone who has just written and published a book
covering Ruby 1.9--I'd really like the 1.9 API to stay stable!)

Also, what is the use case for currying? (Functional programming is not
my strong suit, but I thought that currying was mostly of theoretical
interest and a side effect of the fact that lambda calculus only allowed
single arguments to functions.) Is anyone going to use Proc.curry for
anything other than partial application? If not, wouldn't a papply (or
partial_apply) method be easier and more efficient than the more general
currying?

In that case, Guy's code:

b = a.curry()[1] # parens added for clarity

would become

b = a.papply(1)

David

Yukihiro Matsumoto

unread,
Feb 15, 2008, 9:29:07 AM2/15/08
to ruby...@ruby-lang.org
Hi,

In message "Re: [1.9] Proc#curry"


on Fri, 15 Feb 2008 14:24:33 +0900, David Flanagan <da...@davidflanagan.com> writes:

|I'd like to ask, however, what the rationale is for adding this fairly
|major new piece of API after 1.9.0. I could see this going into the 2.0
|development branch, when it opens, but do we need it in 1.9? (I ask
|this, obviously, as someone who has just written and published a book
|covering Ruby 1.9--I'd really like the 1.9 API to stay stable!)

I consider this method (Proc#curry) to be trivial and should be
treated like an Easter egg for functional programming kids.

matz.

Ripta Pasay

unread,
Feb 15, 2008, 12:15:35 PM2/15/08
to ruby...@ruby-lang.org

On Fri, 15 Feb 2008 14:24:33 +0900, David Flanagan

<da...@davidflanagan.com> wrote:
>
> Also, what is the use case for currying? (Functional programming is not
> my strong suit, but I thought that currying was mostly of theoretical
> interest and a side effect of the fact that lambda calculus only allowed
> single arguments to functions.) Is anyone going to use Proc.curry for
> anything other than partial application? If not, wouldn't a papply (or
> partial_apply) method be easier and more efficient than the more general
> currying?
>

It is my understanding that a partial application is the reverse of
currying: A partial application is a binary function that takes a
multi-argument function and returns a function taking fewer arguments.
Currying, on the other hand, is a unary function that "builds" a
multi-argument function from a series of single-argument functions.

So, currying would be done by calling Proc#curry, while apply is done by
calling Proc#[].


--
Ripta Pasay

Trans

unread,
Feb 15, 2008, 12:35:23 PM2/15/08
to ruby...@ruby-lang.org

Is there a link where I can read about how this new function works?

(granted, I could go digging through the source, but I figure others
might like to know too)

Thanks,
T.

Yukihiro Matsumoto

unread,
Feb 15, 2008, 12:46:00 PM2/15/08
to ruby...@ruby-lang.org
Hi,

In message "Re: Proc#curry"


on Sat, 16 Feb 2008 02:35:23 +0900, Trans <tran...@gmail.com> writes:

|Is there a link where I can read about how this new function works?

[ruby-dev:33676], if you don't mind seeing Japanese ;-)

If you do mind, it's OK. It's not difficult at all,

proc {|x, y, z| x + y + z }.curry

returns the proc object equivalent to

proc {|x| proc {|y| proc {|z| x + y + z } } }

See?

matz.

Gregory Brown

unread,
Feb 15, 2008, 1:35:54 PM2/15/08
to ruby...@ruby-lang.org
On Fri, Feb 15, 2008 at 1:33 PM, Phlip <phli...@gmail.com> wrote:
> Yukihiro Matsumoto wrote:
>
> > proc {|x, y, z| x + y + z }.curry
> >
> > returns the proc object equivalent to
> >
> > proc {|x| proc {|y| proc {|z| x + y + z } } }
> >
> > See?
>
> Uh, how do we call that?
>
> oookay - proc{...}.call(x).call(y).call(z)
>
> What problem does that solve?

this one:

plus_five = proc { |x,y,z| x, y + z }.curry.call(2).call(3)

plus_five[10] #=> 15

Phlip

unread,
Feb 15, 2008, 1:33:08 PM2/15/08
to ruby...@ruby-lang.org
Yukihiro Matsumoto wrote:

> proc {|x, y, z| x + y + z }.curry
>
> returns the proc object equivalent to
>
> proc {|x| proc {|y| proc {|z| x + y + z } } }
>
> See?

Uh, how do we call that?

oookay - proc{...}.call(x).call(y).call(z)

What problem does that solve?

--
Phlip

James Gray

unread,
Feb 15, 2008, 1:54:48 PM2/15/08
to ruby...@ruby-lang.org

Did you mean x + y + z there Greg?

James Edward Gray II

Gregory Brown

unread,
Feb 15, 2008, 2:13:28 PM2/15/08
to ruby...@ruby-lang.org
On Fri, Feb 15, 2008 at 1:54 PM, James Gray <ja...@grayproductions.net> wrote:

> > plus_five = proc { |x,y,z| x, y + z }.curry.call(2).call(3)
>
> Did you mean x + y + z there Greg?

Yeah. whoops.

Thomas Enebo

unread,
Feb 15, 2008, 3:19:04 PM2/15/08
to ruby...@ruby-lang.org
Also in some languages the 2 + 3 can be optimized to only happen once.
Doesn't look like it will here though?

-Tom


Thomas Enebo

unread,
Feb 15, 2008, 3:20:05 PM2/15/08
to ruby...@ruby-lang.org
Ignore the question part of that...I have a minor head code today :)

-Tom

Trans

unread,
Feb 15, 2008, 5:06:30 PM2/15/08
to ruby...@ruby-lang.org

On Feb 15, 12:46 pm, Yukihiro Matsumoto <m...@ruby-lang.org> wrote:
> Hi,
>
> In message "Re: Proc#curry"

> on Sat, 16 Feb 2008 02:35:23 +0900, Trans <transf...@gmail.com> writes:
>
> |Is there a link where I can read about how this new function works?
>
> [ruby-dev:33676], if you don't mind seeing Japanese ;-)
>
> If you do mind, it's OK. It's not difficult at all,
>
> proc {|x, y, z| x + y + z }.curry
>
> returns the proc object equivalent to
>
> proc {|x| proc {|y| proc {|z| x + y + z } } }
>
> See?

Yep. I see.

It is rather trivial, I agree. Unfortunately maybe too trivial because
were stuck with the order of arguments. Or doe curry take some
argument to vary that? To clarify what I mean, Facets has an
implementation of curry, for both Proc and Method, like so:

proc {|x, y, z| x + y + z }.curry(__,__,__)

Of course I like your better in this case ;-) BUT, it does allow:

proc {|x, y, z| x + y + z }.curry(__,5,__)

for:

proc {|x| proc {|z| x + 5 + z } }

T.

Brian Mitchell

unread,
Feb 15, 2008, 8:10:36 PM2/15/08
to ruby...@ruby-lang.org

Isn't that partial application not currying per say (one could curry
as a way to implement partial application)?

Brian.

Trans

unread,
Feb 16, 2008, 7:48:02 AM2/16/08
to ruby...@ruby-lang.org

On Feb 15, 8:10 pm, "Brian Mitchell" <binar...@gmail.com> wrote:

I've also heard it called "partial currying". I'd rather have a
superset of funtionality then a subset. Currying is about isolating a
single argument. It doesn't dictate in which argument to isolate. It
could be x, y, or z, etc. Nor does it mean currying every variable all
the way down that line.

If we give matz's curry function an arity-slot number as an optional
argument, then we could do it all like so:

P = proc { |x,y,z| x+y+z }
P.curry(1) => proc { |y| proc |x,z| x+y+z } }

Which would then allow partial applicaiton via:

P.curry(1)[5]

A second argument could dictate the next level of currying

P.curry(1,1) => proc { |y| proc |z| proc |x| x+y+z } }

T.

Wolfgang Nádasi-Donner

unread,
Feb 16, 2008, 8:18:32 AM2/16/08
to ruby...@ruby-lang.org
Yukihiro Matsumoto schrieb:

> proc {|x, y, z| x + y + z }.curry
>
> returns the proc object equivalent to
>
> proc {|x| proc {|y| proc {|z| x + y + z } } }

This is what I would expect from "currying" (or schönfinkeln). So I
would expect, that the calls will be...

proc {|x, y, z| x + y + z }[1,2,3]
proc {|x, y, z| x + y + z }.curry[1][2][3]

...with the same result, but that...


proc {|x, y, z| x + y + z }.curry[1,2,3]

would fail with the message "3 parameters instead of 1". I'm a little
bit surprized, that ist works...

boviMacBook:~ bovi$ ruby19 --version
ruby 1.9.0 (2008-02-16 revision 0) [i686-darwin9.1.0]
boviMacBook:~ bovi$ irb19
irb(main):001:0> f = proc{|a,b,c|a+b+c}
=> #<Proc:0x3ce9c0@(irb):1>
irb(main):002:0> g = f.curry
=> #<Proc:0x3cc2d8>
irb(main):003:0> f[1,2,3]
=> 6
irb(main):004:0> g[1][2][3]
=> 6
irb(main):005:0> def otto(fun)
irb(main):006:1> fun[3,4]
irb(main):007:1> end
=> nil
irb(main):008:0> r = proc{|k,l|k*l}
=> #<Proc:0x3bc0b8@(irb):8>
irb(main):009:0> s = proc{|m,n,o|m+n+o}.curry
=> #<Proc:0x3b69ec>
irb(main):010:0> otto(r)
=> 12
irb(main):011:0> otto(s[2])
=> 9

Is this an accident or done by intention. If it is done by intention it
is nice, because otherwise I must implement an application which uses
currying with currying all Proc objects that use more than one parameter.

Wolfgang Nádasi-Donner

Lin Jen-Shin

unread,
Feb 16, 2008, 8:20:37 AM2/16/08
to ruby...@ruby-lang.org
On Feb 16, 6:06 am, Trans <transf...@gmail.com> wrote:
> Yep. I see.
>
> It is rather trivial, I agree. Unfortunately maybe too trivial because
> were stuck with the order of arguments. Or doe curry take some
> argument to vary that? To clarify what I mean, Facets has an
> implementation of curry, for both Proc and Method, like so:
>
> proc {|x, y, z| x + y + z }.curry(__,__,__)
>
> Of course I like your better in this case ;-) BUT, it does allow:
>
> proc {|x, y, z| x + y + z }.curry(__,5,__)
>
> for:
>
> proc {|x| proc {|z| x + 5 + z } }
>
> T.

You might want to have a look at gem ludy for Proc#bind,
http://ludy.rubyforge.org/classes/Proc.html#M000042

I'd written Proc#curry as well, so you can write as following:

godfat ~> irb
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'ludy/proc'
=> true
irb(main):003:0> lambda{|x,y,z|[x,y,z]}.curry.bind(:_1, 2, :_2)[1,3]
=> [1, 2, 3]

I am glad to see Proc#curry can be added into core, not supporting
with library,
but I didn't see there's Proc#uncurry as well. I hope there's one too.

Functional programming matters. :)

Trans

unread,
Feb 26, 2008, 1:00:23 PM2/26/08
to ruby...@ruby-lang.org

Here is an example implementation:

class Proc

def curry(*args)
if args.empty?
idx = (0...arity).to_a
else
raise ArgumentError, "argument count is greater than arity
(#{args.size} > #{arity})" if args.size > arity
raise ArgumentError, "arguments must be unique indexes" if
args.uniq != args
raise ArgumentError, "arguments must be indexes" if args.any?
{ |a| !Fixnum===a }
idx = (0...arity).to_a
idx = args + (idx - args)
end

rec = ''
idx.each do |i|
rec << "proc { |a#{i}| "
end
rec << "self["
rec << (0...arity).to_a.collect{|i| "a#{i}"}.join(',')
rec << "]"
rec << "}" * arity

instance_eval rec
end

end

Example:

>> a = proc { |x,y| x**y }
=> #<Proc:0x00002aae4fd50638@(irb):5>
>> b = a.curry(0)
=> #<Proc:0x00002aae4fd4b110@(eval):1>
>> c = a.curry(1)
=> #<Proc:0x00002aae4fd45aa8@(eval):1>
>> b[2][3]
=> 8
>> c[2][3]
=> 9

I'd appreciate suggestions for improvement, as I certainly expect
there are a plenty.

T.

Trans

unread,
Feb 26, 2008, 1:21:15 PM2/26/08
to ruby...@ruby-lang.org

On Feb 16, 8:20 am, Lin Jen-Shin <god...@gmail.com> wrote:

> You might want to have a look at gem ludy for Proc#bind,http://ludy.rubyforge.org/classes/Proc.html#M000042


>
> I'd written Proc#curry as well, so you can write as following:
>
> godfat ~> irb
> irb(main):001:0> require 'rubygems'
> => true
> irb(main):002:0> require 'ludy/proc'
> => true
> irb(main):003:0> lambda{|x,y,z|[x,y,z]}.curry.bind(:_1, 2, :_2)[1,3]
> => [1, 2, 3]

I suspect you did not need the call to #curry there?

Interesting, does it allow .bind(:_1, 2, :_1)[3] ?

> I am glad to see Proc#curry can be added into core, not supporting
> with library,
> but I didn't see there's Proc#uncurry as well. I hope there's one too.

Is it a full reversible processes?

T.

Lin Jen-Shin

unread,
Feb 29, 2008, 10:16:03 AM2/29/08
to ruby...@ruby-lang.org, godfat 真常
On Feb 27, 2:21 am, Trans <transf...@gmail.com> wrote:
> On Feb 16, 8:20 am, Lin Jen-Shin <god...@gmail.com> wrote:
> > godfat ~> irb
> > irb(main):001:0> require 'rubygems'
> > => true
> > irb(main):002:0> require 'ludy/proc'
> > => true
> > irb(main):003:0> lambda{|x,y,z|[x,y,z]}.curry.bind(:_1, 2, :_2)[1,3]
> > => [1, 2, 3]
>
> I suspect you did not need the call to #curry there?

Yes, you're right. I forgot to fix my example there (it's just simply
copied from rdoc), and later I changed the example to:

lambda{|x,y,z|[x,y,z]}.curry.bind(:_1, 2, :_2)[1][3]

I found that it didn't work as I expected, [1,2,3] but nil instead.
It came from calling [1,2,nil][3]. The curry thing didn't work on
bind.
I think I would spend some time on fixing this.

> Interesting, does it allow .bind(:_1, 2, :_1)[3] ?

Yes, it was allowed. The result would be [3,2,3].
Bind just simply creates a lambda which rearranges the arguments
for the calling Proc object.

> > I am glad to see Proc#curry can be added into core, not supporting
> > with library,
> > but I didn't see there's Proc#uncurry as well. I hope there's one too.
>
> Is it a full reversible processes?

Yes, it is. See example in Haskell, which make all function
default to be curried. I won't explain the example here because
this is ruby-core mailing list, neither Haskell nor functional
programming.
I am very glad to see if more FP stuffs are added into ruby core
language.

godfat ~> ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help
Loading package base ... linking ... done.
Prelude> let func = \x y -> x + y
Prelude> func 1 2
3
Prelude> (func 1) 2
3
Prelude> :type func
func :: Integer -> Integer -> Integer

Prelude> uncurry func (1,2)
3
Prelude> :type uncurry func
uncurry func :: (Integer, Integer) -> Integer

Prelude> curry (uncurry func) 1 2
3
Prelude> :type curry (uncurry func)
curry (uncurry func) :: Integer -> Integer -> Integer

> T.

Sean E. Russell

unread,
Mar 1, 2008, 5:45:17 AM3/1/08
to ruby...@ruby-lang.org

Actually, you didn't refute David's implied assertion that the 1.9 API must be
stable, and this makes me nervous.

Isn't Ruby 1.9's API -- by definition -- unstable? If we can't make API
changes in the unstable branches, where can we make them? This is a fairly
important piece of information that developers have to know, and if I've been
making the wrong assumption, I'd like to know.

Thanks.

--
### SER
### Deutsch|Esperanto|Francaise|Linux|XML|Java|Ruby|Haskell|Aikido|Iaido
### http://www.ser1.net, http://www.ser1.net/Security/ser_public.gpg
### Jabber: seaner...@gmail.com (OTR enabled)

Reply all
Reply to author
Forward
0 new messages