Gmail Calendar Documents Reader Web more »
Recently Visited Groups | Help | Sign in
Google Groups Home
macros for generating lambda functions at runtime
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  24 messages - Collapse all  -  Translate all to Translated (View all originals)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Nicolas Edel  
View profile  
 More options Nov 3, 3:06 am
Newsgroups: comp.lang.lisp
From: Nicolas Edel <nicolas.e...@gmail.com>
Date: Mon, 2 Nov 2009 08:06:46 -0800 (PST)
Local: Tues, Nov 3 2009 3:06 am
Subject: macros for generating lambda functions at runtime
Dear group

I'd like to make some closures to speed up operations that are
specified at run time, as it is done for regex compilation. So let's
take a *very* simple exmaple:

CL-USER> (defun foo (value)
           #'(lambda (x)
               (eq x value)))
FOO
CL-USER> foo
#<CLOSURE (LAMBDA (X)) {AF278FD}>
CL-USER> (funcall (foo 1) 0)
NIL
CL-USER> (funcall (bar 1) 1)
T

So that's ok. Now let's suppose we would like the invert the test
depending on a predicate value:

CL-USER> (defun foo (value predicate)
           #'(lambda (x)
               (if predicate
                   (eq x value)
                   (not (eq x value)))))
FOO

Or, in order to avoid a test at runtime:

CL-USER> (defun foo (value predicate)
           (if predicate
               #'(lambda (x) (eq x value))
               #'(lambda (x) (not (eq x value)))))
FOO

It looks like a macro would allow to avoid code duplication here

CL-USER> (defmacro maybe (predicate statement)
           (if predicate
               `(,@statement)
               `(not ,statement)))
MAYBE
CL-USER> (macroexpand-1 '(maybe T (eq x value)))
(EQ X VALUE)
T
CL-USER> (macroexpand-1 '(maybe NIL (eq x value)))
(NOT (EQ X VALUE))
T

Now redefine foo:
CL-USER> (defun foo (value predicate)
           #'(lambda (x)
               (maybe predicate (eq x value))))
;[...]
; caught STYLE-WARNING:
;   The variable PREDICATE is defined but never used.
;[...]
STYLE-WARNING: redefining FOO in DEFUN
FOO

Calling the function shows that the variable PREDICATE is indeed
not taken into account.
Now (finally) the questions:

1. Why ?
2. How to use macros for generating the lambdas ?

Many thanks,

:Nicolas


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
x...@unnamed.xach.com  
View profile  
 More options Nov 3, 3:38 am
Newsgroups: comp.lang.lisp
From: x...@unnamed.xach.com
Date: Mon, 02 Nov 2009 11:38:47 -0500
Local: Tues, Nov 3 2009 3:38 am
Subject: Re: macros for generating lambda functions at runtime

There's another way to avoid code duplication: add a new higher-order
function.

  (defun foo (value)
    (lambda (x)
      (eq x value)))

  (defun predicator (predicate fun)
    (if predicate
        fun
        (complement fun)))

So your second "foo" could be:

  (defun foo-2 (value predicate)
    (predicator predicate (foo value)))

Zach


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
milanj@gmail.com  
View profile  
 More options Nov 3, 3:42 am
Newsgroups: comp.lang.lisp
From: "mil...@gmail.com" <mil...@gmail.com>
Date: Mon, 2 Nov 2009 08:42:41 -0800 (PST)
Local: Tues, Nov 3 2009 3:42 am
Subject: Re: macros for generating lambda functions at runtime
On Nov 2, 5:06 pm, Nicolas Edel <nicolas.e...@gmail.com> wrote:

The answer is in topic name, you cant expand macro at runtime and your
macro expansion depends on runtime value (predicate)

    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
joswig@corporate-world.li sp.de  
View profile  
 More options Nov 3, 3:46 am
Newsgroups: comp.lang.lisp
From: "jos...@corporate-world.lisp.de" <jos...@lisp.de>
Date: Mon, 2 Nov 2009 08:46:56 -0800 (PST)
Local: Tues, Nov 3 2009 3:46 am
Subject: Re: macros for generating lambda functions at runtime
On 2 Nov., 17:06, Nicolas Edel <nicolas.e...@gmail.com> wrote:

> Dear group

> I'd like to make some closures to speed up operations that are
> specified at run time, as it is done for regex compilation. So let's
> take a *very* simple exmaple:

> CL-USER> (defun foo (value)
>            #'(lambda (x)
>                (eq x value)))
> FOO
> CL-USER> foo
> #<CLOSURE (LAMBDA (X)) {AF278FD}>
> CL-USER> (funcall (foo 1) 0)
> NIL
> CL-USER> (funcall (bar 1) 1)

Numbers are compared with EQL, not EQ.

(if predicate ...) - you are checking a macro expansion time for the
value of predicate.

Not a good idea.

You think you now a macro expansion time the value of predicate, but
you don't. All you know is the code, but not its value.

Try:   `(if ,predicate ...  )

> MAYBE
> CL-USER> (macroexpand-1 '(maybe T (eq x value)))
> (EQ X VALUE)
> T
> CL-USER> (macroexpand-1 '(maybe NIL (eq x value)))
> (NOT (EQ X VALUE))
> T

> Now redefine foo:
> CL-USER> (defun foo (value predicate)
>            #'(lambda (x)
>                (maybe predicate (eq x value))))

The macro maybe is used.

It has two parameters: predicate and statement.

The arguments are predicate and (eq x value)

predicate = predicate

statement = (eq x value)

Remember above

(if predicate ... )   ?

predicate evaluates to the symbol predicate, which is always true.

It is similar to (if 'predicate ...) ....


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Captain Obvious  
View profile  
 More options Nov 3, 5:24 am
Newsgroups: comp.lang.lisp
From: "Captain Obvious" <udode...@users.sourceforge.net>
Date: Mon, 2 Nov 2009 20:24:30 +0200
Local: Tues, Nov 3 2009 5:24 am
Subject: Re: macros for generating lambda functions at runtime
 NE> 1. Why ?

Because macros work at macroexpansion time, NOT
runtime.

 NE> 2. How to use macros for generating the lambdas ?

You can use macro to generate a code which generates lambda,
for example.


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Nicolas Edel  
View profile  
 More options Nov 3, 7:16 pm
Newsgroups: comp.lang.lisp
From: Nicolas Edel <nicolas.e...@gmail.com>
Date: Tue, 3 Nov 2009 00:16:08 -0800 (PST)
Local: Tues, Nov 3 2009 7:16 pm
Subject: Re: macros for generating lambda functions at runtime
On Nov 2, 7:24 pm, "Captain Obvious" <udode...@users.sourceforge.net>
wrote:

>  NE> 1. Why ?

> Because macros work at macroexpansion time, NOT
> runtime.

>  NE> 2. How to use macros for generating the lambdas ?

> You can use macro to generate a code which generates lambda,
> for example.

Ok, so I use macro to generate code and then invoke the compiler at
runtime.
What is best practice for this: eval or compile ? Do they have similar
meaning in this context ?

(defun foo (predicate value)
           (eval `(function (lambda (x)
                    (maybe ,predicate (eql x ,value))))))

or

(defun foo (predicate value)
           (compile nil `(lambda (x)
                           (maybe ,predicate (eql x ,value)))))

:Nicolas


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pascal Costanza  
View profile  
 More options Nov 3, 7:31 pm
Newsgroups: comp.lang.lisp
From: Pascal Costanza <p...@p-cos.net>
Date: Tue, 03 Nov 2009 09:31:54 +0100
Local: Tues, Nov 3 2009 7:31 pm
Subject: Re: macros for generating lambda functions at runtime

The semantics are the same. Which one is better depends on context. If
you use these functions only one or two times, or so, it's not
worthwhile to compile them, because compilation creates a considerable
overhead by itself. If you use them more often, then compilation is
worthwhile (although I recently heard that in, say, ECL or GCL,
compilation is almost always prohibitive).

You can also say (coerce '(lambda ...) 'function), and let the CL
implementation itself decide.

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Nicolas Edel  
View profile  
 More options Nov 3, 7:46 pm
Newsgroups: comp.lang.lisp
From: Nicolas Edel <nicolas.e...@gmail.com>
Date: Tue, 3 Nov 2009 00:46:38 -0800 (PST)
Local: Tues, Nov 3 2009 7:46 pm
Subject: Re: macros for generating lambda functions at runtime
On Nov 3, 9:31 am, Pascal Costanza <p...@p-cos.net> wrote:

At that time, I don't know if they will be used often. But if they are
to be used in a context such as packet filtering, there will be a slow
startup time but this will be runtime efficient. I wanted to learn how
to make this now to avoid problems at experimentation time.

> You can also say (coerce '(lambda ...) 'function), and let the CL
> implementation itself decide.

Nice, indeed. But before I let the implementation decide by itself, I
would like to know if there are fundamental differences when calling
eval and compile in this context.

:Nicolas


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Barry Margolin  
View profile  
 More options Nov 3, 8:07 pm
Newsgroups: comp.lang.lisp
From: Barry Margolin <bar...@alum.mit.edu>
Date: Tue, 03 Nov 2009 04:07:38 -0500
Local: Tues, Nov 3 2009 8:07 pm
Subject: Re: macros for generating lambda functions at runtime
In article
<b4f59fea-7673-4a75-91f4-ca3983a24...@k17g2000yqb.googlegroups.com>,
 Nicolas Edel <nicolas.e...@gmail.com> wrote:

No semantic differences, just performance differences.  Compile will
create a compiled function, eval will create an interpreted function in
some implementations.

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Tobias C. Rittweiler  
View profile  
 More options Nov 3, 8:21 pm
Newsgroups: comp.lang.lisp
From: "Tobias C. Rittweiler" <t...@freebits.de.invalid>
Date: Tue, 03 Nov 2009 10:21:55 +0100
Local: Tues, Nov 3 2009 8:21 pm
Subject: Re: macros for generating lambda functions at runtime

Barry Margolin <bar...@alum.mit.edu> writes:
>  Nicolas Edel <nicolas.e...@gmail.com> wrote:

> > Nice, indeed. But before I let the implementation decide by itself, I
> > would like to know if there are fundamental differences when calling
> > eval and compile in this context.

> No semantic differences, just performance differences.  Compile will
> create a compiled function, eval will create an interpreted function in
> some implementations.

COMPILED-FUNCTION-P will return T on the first kind, but NIL on the
other kind! That's as big semantical change as you can get. ;-)

  -T.


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Captain Obvious  
View profile  
 More options Nov 3, 8:25 pm
Newsgroups: comp.lang.lisp
From: "Captain Obvious" <udode...@users.sourceforge.net>
Date: Tue, 3 Nov 2009 11:25:25 +0200
Local: Tues, Nov 3 2009 8:25 pm
Subject: Re: macros for generating lambda functions at runtime
 ??>> You can use macro to generate a code which generates lambda,
 ??>> for example.

 NE> Ok, so I use macro to generate code and then invoke the compiler at
 NE> runtime.

No! You use macro to transform your code at compile time.
Then code you've generated does anything it wants in runtime.

 NE> What is best practice for this: eval or compile ?

In case you _really_ need it, best practice is compile.
But you do not need it in this case.
A rule of thumb: if you think you need eval or compile,
it is higly lickely you're doing it wrong.

Basically, the only case when you need eval is when
code is not known at compile-time.  That is, user
enters it from keyboard, or you recieve it from network,
or generate through GA or something. In other cases,
when you just need to fill in some parameters, you
can do that other way.

 NE>  Do they have similar meaning in this context ?

Yep they do, almost. The only difference is that
eval might or might not compile functions, while compile
almost certainly does. compile is a bit tidier IMHO
because it accepts only lambda rather than arbitrary
piece of code. There is also a third way to do it -- (coerce ... 'function),
it is pretty good too, if you just need a function, not necessarily
compiled.

That's how you can generate lambdas at runtime,
but there is no reason to do this in this case.

Xach have shown how you can do this with lambda
and without any code duplication. That was really the best way.
It is actually a good practice to avoid using macros when
you can just use higher-order functions.

If you absolutely want to use macros, you can do this like
this (using IF at runtime):

(defmacro maybe-lambda (predicate paramlist statement)
      `(if ,predicate
                (lambda ,paramlist ,statement)
                (lambda ,paramlist (not ,statement))))

(defun foo (value predicate)
  (maybe-lambda predicate (x) (eql x value)))

No runtime code generation, but maybe have lost its generality.

Or you can write a more complex macro which is more general.
Your `maybe` macro just maybe  inserts NOT in the beginning
of the statement.
My with-maybe macro can insert NOT into any place within
statement user wants it to.

(defmacro with-maybe (predicate statement)
  `(if ,predicate
    (macrolet ((maybe (statement) statement))
      ,statement)
    (macrolet ((maybe (statement) `(not ,statement)))
      ,statement)))

You use it like this:

(defun foo (value predicate)
  (with-maybe predicate
    (lambda (x) (maybe (eql x value)))))

Macro generates a code which has IF switching between two
branches. Branches are made different by different MAYBE
local macros defined via MACROLET -- in first branch MAYBE is no-op,
so it is vanilla statement. In second branch MAYBE adds NOT to
the statement.

Unfortunately there is no tool one can use to get fully macroexpanded
code (at least I don't know any), but once you'll replace macrolet:ed
maybe:s, you'll get exactly function you had wrote yourself:

(defun foo (value predicate)
       (if predicate
            (lambda (x) (eq x value))
            (lambda (x) (not (eq x value)))))

 NE> (defun foo (predicate value)
 NE>     (eval `(function (lambda (x)
 NE>       (maybe ,predicate (eql x ,value))))))

By the way, if you're generating code anyway, you do not really
need macro, you can do this with ordinary function or just construct
in-place:

 (defun foo (predicate value)
     (eval `(function (lambda (x)
            ,(let ((code `(eql x, value))
               (if predicate
                   code
                   `(not ,code)))))


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pascal Costanza  
View profile  
 More options Nov 3, 9:47 pm
Newsgroups: comp.lang.lisp
From: Pascal Costanza <p...@p-cos.net>
Date: Tue, 03 Nov 2009 11:47:27 +0100
Local: Tues, Nov 3 2009 9:47 pm
Subject: Re: macros for generating lambda functions at runtime

Tobias C. Rittweiler wrote:
> Barry Margolin <bar...@alum.mit.edu> writes:

>>  Nicolas Edel <nicolas.e...@gmail.com> wrote:

>>> Nice, indeed. But before I let the implementation decide by itself, I
>>> would like to know if there are fundamental differences when calling
>>> eval and compile in this context.
>> No semantic differences, just performance differences.  Compile will
>> create a compiled function, eval will create an interpreted function in
>> some implementations.

> COMPILED-FUNCTION-P will return T on the first kind, but NIL on the
> other kind!

Not quite: It may actually return T in some implementations for the
second kind. A CL implementation is required to support at least minimal
compilation (as per 3.2.2.2 in the HyperSpec), but is not required to
support a 'pure' interpreter (as per 3.1 in the HyperSpec) and can
actually always perform (at least minimal) compilation.

> That's as big semantical change as you can get. ;-)

Still not that big. ;)

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pascal Costanza  
View profile  
 More options Nov 3, 10:02 pm
Newsgroups: comp.lang.lisp
From: Pascal Costanza <p...@p-cos.net>
Date: Tue, 03 Nov 2009 12:02:41 +0100
Local: Tues, Nov 3 2009 10:02 pm
Subject: Re: macros for generating lambda functions at runtime

If you want your code to be very sophisticated, you can actually also
implement your own version that uses interpreted code by default, counts
how often a particular runtime-generated function is called, and after a
certain threshold, compiles into a more efficient version (under the
assumption that it will continue to be called even more often).

Here is a sketch [untested]:

(defclass smart-function (funcallable-standard-object) ()
   (:metaclass funcallable-standard-class))

(defparameter *threshold* 10)

(defun make-smart-function (body)
   (let ((fun (make-instance 'smart-function)))
     (set-funcallable-instance-function
       fun
       (let ((counter 0) (funfun (eval body)))
         (lambda (&rest args)
           (declare (dynamic-extent args)
           (when (> (incf counter) *threshold*)
             (unless (compiled-function-p funfun)
               (setq funfun (compile nil body)))
             (set-funcallable-instance-function fun funfun))
           (apply funfun args))))
     fun))

(defmacro smart-lambda ((&rest args) &body body)
   `(make-smart-function `(lambda ,args ,@body)))

Yes, this requires the CLOS MOP. ;)

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Nicolas Edel  
View profile  
 More options Nov 3, 11:37 pm
Newsgroups: comp.lang.lisp
From: Nicolas Edel <nicolas.e...@gmail.com>
Date: Tue, 3 Nov 2009 04:37:08 -0800 (PST)
Local: Tues, Nov 3 2009 11:37 pm
Subject: Re: macros for generating lambda functions at runtime
On Nov 3, 6:02 am, Pascal Costanza <p...@p-cos.net> wrote:

Looks nice, but I am not shure I am already comfortable enough at Lisp
programing to use such tricks. Thanks anyway ;)

:Nicolas


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Juanjo  
View profile  
 More options Nov 4, 12:07 am
Newsgroups: comp.lang.lisp
From: Juanjo <juanjose.garciarip...@googlemail.com>
Date: Tue, 3 Nov 2009 05:07:23 -0800 (PST)
Local: Wed, Nov 4 2009 12:07 am
Subject: Re: macros for generating lambda functions at runtime
On Nov 3, 9:31 am, Pascal Costanza <p...@p-cos.net> wrote:

It is not "almost always prohibitive". The cost is basically executing
a C compiler. And that is worthwhile if you need the function often
and if the function it is low level enough that it profits from
compilation.

What is prohibitive is to compile using C a function that you are
going to use only once, and do it for every use.

The alternative then is to use bytcompiled functions, which do save
some time, but are not as efficient as natively compiled ones. You can
always do this, without much hassle. One cheap way is to coerce a
lambda form to a function type.

> (coerce '(lambda (x) (1+ x)) 'function)

#<bytecompiled-function 0000000102a301e0>

> (funcall * 2)

3

Juanjo


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pascal Costanza  
View profile  
 More options Nov 4, 1:25 am
Newsgroups: comp.lang.lisp
From: Pascal Costanza <p...@p-cos.net>
Date: Tue, 03 Nov 2009 15:25:55 +0100
Local: Wed, Nov 4 2009 1:25 am
Subject: Re: macros for generating lambda functions at runtime

OK, thanks a lot for the clarification, and also for the hint about byte
compilation, which is certainly useful!

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pillsy  
View profile  
 More options Nov 4, 2:59 am
Newsgroups: comp.lang.lisp
From: Pillsy <pillsb...@gmail.com>
Date: Tue, 3 Nov 2009 07:59:31 -0800 (PST)
Local: Wed, Nov 4 2009 2:59 am
Subject: Re: macros for generating lambda functions at runtime
On Nov 3, 6:02 am, Pascal Costanza <p...@p-cos.net> wrote:
[...]

What's the advantage of using the MOP over a straight-up closure in
this instance?

Thanks,
Pillsy


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pascal Costanza  
View profile  
 More options Nov 4, 3:14 am
Newsgroups: comp.lang.lisp
From: Pascal Costanza <p...@p-cos.net>
Date: Tue, 03 Nov 2009 17:14:20 +0100
Local: Wed, Nov 4 2009 3:14 am
Subject: Re: macros for generating lambda functions at runtime

Good question. Maybe there is none. Originally I thought I had to store
the body in a slot of the smart-function instance, but that turned out
not to be necessary. So indeed, maybe you can replace this by a plain
closure.

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pascal Costanza  
View profile  
 More options Nov 4, 3:58 am
Newsgroups: comp.lang.lisp
From: Pascal Costanza <p...@p-cos.net>
Date: Tue, 03 Nov 2009 17:58:01 +0100
Local: Wed, Nov 4 2009 3:58 am
Subject: Re: macros for generating lambda functions at runtime

Yes, I guess this should work:

(defparameter *threshold* 10)

(defun make-smart-function (body)
   (let ((counter 0) fun funfun)
     (setq funfun (eval body)
           fun (lambda (&rest args)
                 (declare (dynamic-extent args))
                 (when (> (incf counter) *threshold*)
                   (unless (compiled-function-p funfun)
                     (setq funfun (compile nil body))
                     (setq fun funfun)))
                 (apply funfun args)))
     (lambda (&rest args)
       (declare (dynamic-extent args))
       (apply fun args))))

(defmacro smart-lambda ((&rest args) &body body)
   `(make-smart-function (lambda ,args ,@body)))

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Captain Obvious  
View profile  
 More options Nov 4, 9:21 am
Newsgroups: comp.lang.lisp
From: "Captain Obvious" <udode...@users.sourceforge.net>
Date: Wed, 4 Nov 2009 00:21:27 +0200
Local: Wed, Nov 4 2009 9:21 am
Subject: Re: macros for generating lambda functions at runtime
 P> What's the advantage of using the MOP over a straight-up closure in
 P> this instance?

Might have better performance. If you use &rest and then apply
I guess most compilers will make a list, that is, you have more consing.

(lambda (&rest args)
   (let ((fn ....))
      (apply fn args)))

But if you're doing it via macro, you can use certain number of parameters
directly:

(lambda (a b c)
   (let ((fn ....))
      (funcall fn a b c)))

Then  there is no consing. There might be some overhead from using closure's
variable and funcall, but I guess funcallable-object's funcall is not
totally overheadless
either. So probably funcallable-object offers only a minuscule advantage in
this case.


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Kaz Kylheku  
View profile  
 More options Nov 5, 12:25 pm
Newsgroups: comp.lang.lisp
From: Kaz Kylheku <kkylh...@gmail.com>
Date: Thu, 5 Nov 2009 01:25:51 +0000 (UTC)
Local: Thurs, Nov 5 2009 12:25 pm
Subject: Re: macros for generating lambda functions at runtime
On 2009-11-03, Captain Obvious <udode...@users.sourceforge.net> wrote:

>  P> What's the advantage of using the MOP over a straight-up closure in
>  P> this instance?

> Might have better performance. If you use &rest and then apply
> I guess most compilers will make a list, that is, you have more consing.

> (lambda (&rest args)
>    (let ((fn ....))
>       (apply fn args)))

It's a poor Lisp compiler which conses a list for code which only passes the
&rest argument down to another function with apply.

Pascal's code went right over your head there. The advantage of the MOP
solution has nothing to do with this. And it has &rest/apply processing buried
in it.

What Pascal's code does is allow the user's function to be called several times
without being compiled. It counts how many calls there are. When the calls
exceed a threshold, then the object compiles the function.

When the function is compiled, the object is able to replace its own closure
with that compiled function. So subsequent calls go directly to that function,
bypassing the threshold check.

You can't do that with a lambda closure, because a lambda closure cannot
replace future calls to itself with a different closure.


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Kaz Kylheku  
View profile  
 More options Nov 5, 12:39 pm
Newsgroups: comp.lang.lisp
From: Kaz Kylheku <kkylh...@gmail.com>
Date: Thu, 5 Nov 2009 01:39:09 +0000 (UTC)
Local: Thurs, Nov 5 2009 12:39 pm
Subject: Re: macros for generating lambda functions at runtime
On 2009-11-03, Pascal Costanza <p...@p-cos.net> wrote:

Uh, you can do this with the existing standard funcallable objects.  Namely,
instances of this highly curious SYMBOL class, which have a standard
slot given by the accessor SYMBOL-FUNCTION. :)

;; tested modifications to untested code
;; fixed unbalanced parentheses in declare
;; extra levels of unquote added to backquote
;; class replaced by symbol

(defparameter *threshold* 10)

(defun make-smart-function (body)
  (let ((fun (gensym "SMART-FUNCTION-")))
    (setf
      (symbol-function fun)
      (let ((counter 0) (funfun (eval body)))
        (lambda (&rest args)
          (declare (dynamic-extent args))
          (when (> (incf counter) *threshold*)
            (unless (compiled-function-p funfun)
              (setq funfun (compile nil body)))
            (setf (symbol-function fun) funfun))
          (apply funfun args))))
    fun))

(defmacro smart-lambda ((&rest args) &body body)
  `(make-smart-function `(lambda ,',args ,',@body)))

Test:

[4]> (defparameter x (smart-lambda (a b c) (list a b c)))
X
[5]> (symbol-function x)
#<FUNCTION :LAMBDA (&REST ARGS) (DECLARE (DYNAMIC-EXTENT ARGS))
  (WHEN (> (INCF COUNTER) *THRESHOLD*)
   (UNLESS (COMPILED-FUNCTION-P FUNFUN) (SETQ FUNFUN (COMPILE NIL BODY)))
   (SETF (SYMBOL-FUNCTION FUN) FUNFUN))
  (APPLY FUNFUN ARGS)>
[6]> (funcall x 1 2 3)
(1 2 3)
[7]> (funcall x 1 2 3)
(1 2 3)
[8]> (symbol-function x)
#<FUNCTION :LAMBDA (&REST ARGS) (DECLARE (DYNAMIC-EXTENT ARGS))
  (WHEN (> (INCF COUNTER) *THRESHOLD*)
   (UNLESS (COMPILED-FUNCTION-P FUNFUN) (SETQ FUNFUN (COMPILE NIL BODY)))
   (SETF (SYMBOL-FUNCTION FUN) FUNFUN))
  (APPLY FUNFUN ARGS)>
[9]> *threshold*
10
[10]> (symbol-function x)
#<FUNCTION :LAMBDA (&REST ARGS) (DECLARE (DYNAMIC-EXTENT ARGS))
  (WHEN (> (INCF COUNTER) *THRESHOLD*)
   (UNLESS (COMPILED-FUNCTION-P FUNFUN) (SETQ FUNFUN (COMPILE NIL BODY)))
   (SETF (SYMBOL-FUNCTION FUN) FUNFUN))
  (APPLY FUNFUN ARGS)>
[11]> (funcall x 1 2 3)
(1 2 3)
[12]> (funcall x 1 2 3)
(1 2 3)
[13]> (funcall x 1 2 3)
(1 2 3)
[14]> (funcall x 1 2 3)
(1 2 3)
[15]> (funcall x 1 2 3)
(1 2 3)
[16]> (funcall x 1 2 3)
(1 2 3)
[17]> (funcall x 1 2 3)
(1 2 3)
[18]> (funcall x 1 2 3)
(1 2 3)
[19]> (funcall x 1 2 3)
(1 2 3)
[20]> (funcall x 1 2 3)
(1 2 3)
[21]> (funcall x 1 2 3)
(1 2 3)
[22]> (symbol-function x)
#<COMPILED-FUNCTION NIL>
[23]> (funcall x 1 2 3)
(1 2 3)


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pascal Costanza  
View profile  
 More options Nov 5, 7:56 pm
Newsgroups: comp.lang.lisp
From: Pascal Costanza <p...@p-cos.net>
Date: Thu, 05 Nov 2009 09:56:18 +0100
Local: Thurs, Nov 5 2009 7:56 pm
Subject: Re: macros for generating lambda functions at runtime

In principle yes, but unfortunately, there is a border case that doesn't
quite work with symbols as functions:

(defclass my-fun (funcallable-standard-object)
   ()
   (:metaclass funcallable-standard-class))

(defmethod initialize-instance :after ((f my-fun) &key)
   (set-funcallable-instance-function f (lambda () 42)))

* (setf (symbol-function 'foo) (make-instance 'my-fun))

#<MY-FUN {1002F44C09}>
* (foo)

42
* (setf (symbol-function 'bar) 'foo)
; in: LAMBDA NIL
;     (FUNCALL #'(SETF SYMBOL-FUNCTION) #:NEW671
'CLOSER-COMMON-LISP-USER::BAR)
; --> SB-C::%FUNCALL
; ==>
;   (#<SB-C::GLOBAL-VAR
;      :%SOURCE-NAME (SETF SYMBOL-FUNCTION)
;      :TYPE #<SB-KERNEL:FUN-TYPE (FUNCTION #'SYMBOL #)>
;      :WHERE-FROM :DECLARED
;      :KIND :GLOBAL-FUNCTION {1002F4A281}>
;    #:NEW671 'CLOSER-COMMON-LISP-USER::BAR)
;
; caught WARNING:
;   Asserted type FUNCTION conflicts with derived type
;   (VALUES (MEMBER FOO) &OPTIONAL).
;   See also:
;     The SBCL Manual, Node "Handling of Types"
;
; compilation unit finished
;   caught 1 WARNING condition

debugger invoked on a TYPE-ERROR: The value FOO is not of type FUNCTION.

Some Common Lisp implementations accept this, some don't. Those that
don't accept this are right not to do so, according to the HyperSpec. (I
think this is an unnecessary restriction, symbols should be accepted as
symbol-functions, but that's maybe just me... ;)

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Captain Obvious  
View profile  
 More options Nov 6, 11:33 pm
Newsgroups: comp.lang.lisp
From: "Captain Obvious" <udode...@users.sourceforge.net>
Date: Fri, 6 Nov 2009 14:33:25 +0200
Local: Fri, Nov 6 2009 11:33 pm
Subject: Re: macros for generating lambda functions at runtime
 ??>> Might have better performance. If you use &rest and then apply
 ??>> I guess most compilers will make a list, that is, you have more
 ??>> consing.
 ??>>
 ??>> (lambda (&rest args)
 ??>>    (let ((fn ....))
 ??>>       (apply fn args)))

 KK> It's a poor Lisp compiler which conses a list for code which only
 KK> passes the &rest argument down to another function with apply.

Can you please show an example of a compiler which optimizes it?

 KK> You can't do that with a lambda closure, because a lambda closure
 KK> cannot replace future calls to itself with a different closure.

That's why we have not one closure, but two closures, with first
calling the second one -- then we can replace second closure with
a compiled version when we would want to. There is overhead
of indirection, but arguably it is very low.

Here's code by Pascal:

(defparameter *threshold* 10)

(defun make-smart-function (body)
   (let ((counter 0) fun funfun)
     (setq funfun (eval body)
           fun (lambda (&rest args)
                 (declare (dynamic-extent args))
                 (when (> (incf counter) *threshold*)
                   (unless (compiled-function-p funfun)
                     (setq funfun (compile nil body))
                     (setq fun funfun)))
                 (apply funfun args)))
     (lambda (&rest args)
       (declare (dynamic-extent args))
       (apply fun args))))

(defmacro smart-lambda ((&rest args) &body body)
   `(make-smart-function (lambda ,args ,@body)))

Let's see does it go over your head or not.


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
End of messages
« Back to Discussions « Newer topic     Older topic »

Create a group - Google Groups - Google Home - Terms of Service - Privacy Policy
©2009 Google