From: Pascal Costanza on
Juanjo wrote:
> On Nov 3, 9:31 am, Pascal Costanza <p...(a)p-cos.net> wrote:
>> Nicolas Edel wrote:
>>> On Nov 2, 7:24 pm, "Captain Obvious" <udode...(a)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)))))
>> 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,ECLor GCL,
>> compilation is almost always prohibitive).
>
> 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

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/
From: Pillsy on
On Nov 3, 6:02 am, Pascal Costanza <p...(a)p-cos.net> wrote:
[...]
> 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))
[...]
> Yes, this requires the CLOS MOP. ;)

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

Thanks,
Pillsy
From: Pascal Costanza on
Pillsy wrote:
> On Nov 3, 6:02 am, Pascal Costanza <p...(a)p-cos.net> wrote:
> [...]
>> 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))
> [...]
>> Yes, this requires the CLOS MOP. ;)
>
> What's the advantage of using the MOP over a straight-up closure in
> this instance?

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/
From: Pascal Costanza on
Pascal Costanza wrote:
> Pillsy wrote:
>> On Nov 3, 6:02 am, Pascal Costanza <p...(a)p-cos.net> wrote:
>> [...]
>>> 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))
>> [...]
>>> Yes, this requires the CLOS MOP. ;)
>>
>> What's the advantage of using the MOP over a straight-up closure in
>> this instance?
>
> 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.

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/
From: Captain Obvious on
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.