From: Moop
Subject: a cheap curry macro
Date: 
Message-ID: <87n0g5bja4.fsf@foo.foo>
Hi lispers,

So I'm a bit confused. I wrote this macro:

(defmacro curry (fn &rest args)
  `(lambda (&rest argz)
     (apply ,fn ,@args argz)))

and then I test it's expansion:

* (macroexpand-1 '(curry #'+ 1))
(LAMBDA (&REST ARGZ) (APPLY #'+ 1 ARGZ))
T
* 

Looks good. But if I try this:

* ((curry #' + 1) 1)

I get:

; Error: Illegal function call.

but if I do:

* (funcall (curry #' + 1) 1)
2
*

It works. So if curry expands to a lambda function then why can't I
use ((curry #' + 1) 1)? Why must I funcall it? Am I messing up the
macro definition in some way?

Thanks,
Shawn

From: Kalle Olavi Niemitalo
Subject: Re: a cheap curry macro
Date: 
Message-ID: <873chxz6el.fsf@Astalo.kon.iki.fi>
Moop <····@moop.moop> writes:

> So if curry expands to a lambda function then why can't I
> use ((curry #' + 1) 1)?

3.1.2.1.2: "If the car of a compound form is not a symbol, then
that car must be a lambda expression, in which case the compound
form is a lambda form."

In your compound form, the car is a macro form, not a lambda
expression.  Common Lisp does not allow this.  I think Scheme does.
From: Justin Dubs
Subject: Re: a cheap curry macro
Date: 
Message-ID: <2e262238.0306260915.9c6e916@posting.google.com>
Currying doesn't need to be implemented as a macro.  Here's how I do
it, and apparently also Paul Graham as I saw this code in On Lisp as
well:

(defun curry (fn &rest args)
  (lambda (&rest more-args)
    (apply fn (append args more-args))))

You can just as easily implement right-currying:

(defun rcurry (fn &rest args)
  (lambda (&rest more-args)
    (apply fn (append more-args args))))

The only difference is the order of args to the append.

Hope this helps,

Justin Dubs
From: Nils Goesche
Subject: Re: a cheap curry macro
Date: 
Message-ID: <87d6h0n463.fsf@darkstar.cartan>
······@eos.ncsu.edu (Justin Dubs) writes:

> Currying doesn't need to be implemented as a macro.  Here's how
> I do it, and apparently also Paul Graham as I saw this code in
> On Lisp as well:
> 
> (defun curry (fn &rest args)
>   (lambda (&rest more-args)
>     (apply fn (append args more-args))))

The disadvantage is that this conses each time the curried
function is called.

Regards,
-- 
Nils G�sche
Ask not for whom the <CONTROL-G> tolls.

PGP key ID #xD26EF2A0
From: Kent M Pitman
Subject: Re: a cheap curry macro
Date: 
Message-ID: <sfw3chw8t6g.fsf@shell01.TheWorld.com>
Nils Goesche <···@cartan.de> writes:

> ······@eos.ncsu.edu (Justin Dubs) writes:
> 
> > Currying doesn't need to be implemented as a macro.  Here's how
> > I do it, and apparently also Paul Graham as I saw this code in
> > On Lisp as well:
> > 
> > (defun curry (fn &rest args)
> >   (lambda (&rest more-args)
> >     (apply fn (append args more-args))))
> 
> The disadvantage is that this conses each time the curried
> function is called.

Well, it's easily possible to recursively rewrite this in a way that
does not cons [at curried function execution time, I mean] in at
least some implementations.
From: Nils Goesche
Subject: Re: a cheap curry macro
Date: 
Message-ID: <878yromu2n.fsf@darkstar.cartan>
Kent M Pitman <······@world.std.com> writes:

> Nils Goesche <···@cartan.de> writes:
> 
> > ······@eos.ncsu.edu (Justin Dubs) writes:
> > 
> > > Currying doesn't need to be implemented as a macro.  Here's how
> > > I do it, and apparently also Paul Graham as I saw this code in
> > > On Lisp as well:
> > > 
> > > (defun curry (fn &rest args)
> > >   (lambda (&rest more-args)
> > >     (apply fn (append args more-args))))
> > 
> > The disadvantage is that this conses each time the curried
> > function is called.
> 
> Well, it's easily possible to recursively rewrite this in a way
> that does not cons [at curried function execution time, I mean]
> in at least some implementations.

Yes, but then you have more function calls each time you call the
curried function, and more closures consed up at the time you
build it.

Regards,
-- 
Nils G�sche
Ask not for whom the <CONTROL-G> tolls.

PGP key ID #xD26EF2A0
From: Kent M Pitman
Subject: Re: a cheap curry macro
Date: 
Message-ID: <sfwvfussen5.fsf@shell01.TheWorld.com>
Nils Goesche <···@cartan.de> writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> > Nils Goesche <···@cartan.de> writes:
> > 
> > > ······@eos.ncsu.edu (Justin Dubs) writes:
> > > 
> > > > Currying doesn't need to be implemented as a macro.  Here's how
> > > > I do it, and apparently also Paul Graham as I saw this code in
> > > > On Lisp as well:
> > > > 
> > > > (defun curry (fn &rest args)
> > > >   (lambda (&rest more-args)
> > > >     (apply fn (append args more-args))))
> > > 
> > > The disadvantage is that this conses each time the curried
> > > function is called.
> > 
> > Well, it's easily possible to recursively rewrite this in a way
> > that does not cons [at curried function execution time, I mean]
> > in at least some implementations.
> 
> Yes, but then you have more function calls each time you call the
> curried function, and more closures consed up at the time you
> build it.

Not if they are downward (i.e., dynamic-extent) closures and/or
downward conses.
From: Nils Goesche
Subject: Re: a cheap curry macro
Date: 
Message-ID: <87llvonayo.fsf@darkstar.cartan>
Moop <····@moop.moop> writes:

> (defmacro curry (fn &rest args)
>   `(lambda (&rest argz)
>      (apply ,fn ,@args argz)))
> 
> and then I test it's expansion:
> 
> * (macroexpand-1 '(curry #'+ 1))
> (LAMBDA (&REST ARGZ) (APPLY #'+ 1 ARGZ))
> T

Kalle already answered your question, but I'd like to point out
that there are two further problems with this macro:


CL-USER 11 > (macroexpand-1 '(curry (foo 2) argz))
(LAMBDA (&REST ARGZ) (APPLY (FOO 2) ARGZ ARGZ))
T

First, (foo 2) is evaluated each time the curried function is
called; this /might/ be what you want, but I guess not.
Moreover, your local variable ARGZ might be clobbered.  My
version of CURRY is:

(defmacro curry (f &rest curry-args)
  (let ((args (gensym "ARGS"))
        (fun (gensym "FUN"))
        (curries (mapcar (lambda (arg)
                           (declare (ignore arg))
                           (gensym))
                         curry-args)))
    `(let ((,fun ,f)
           ,@(mapcar #'list curries curry-args))
       (lambda (&rest ,args)
         (declare (dynamic-extent ,args))
         (apply ,fun ,@curries ,args)))))

The DYNAMIC-EXTENT declaration might prevent that the list bound
to ,args is consed up on the heap in some implementations.

Regards,
-- 
Nils G�sche
Ask not for whom the <CONTROL-G> tolls.

PGP key ID #xD26EF2A0