From: Mirko on
Hello,

I would like to use a macro to expand into a function body, but it
needs to have a declare statement at the top. Here is the macro:

(defmacro dist (fun)
`(progn
(declare (number lo hi))
(coerce lo 'double-float)
(coerce hi 'double-float)
(make-marray 'double-float
:initial-contents
(coerce (,fun lo hi count) 'list))))

and one example of its use:

(defun useq (lo hi &optional (count 51))
(dist my-utils:rseq))

The function compilation fails, because the `declare' form is not the
first form in the function body. It is nested behind `progn'

1) how to accomplish this with a macro?
2) Or should I use a function instead, passing `fun' as an argument?

Thank you,

Mirko
From: Tamas K Papp on
On Thu, 04 Mar 2010 09:40:39 -0800, Mirko wrote:

> Hello,
>
> I would like to use a macro to expand into a function body, but it needs
> to have a declare statement at the top. Here is the macro:
>
> (defmacro dist (fun)
> `(progn
> (declare (number lo hi))
> (coerce lo 'double-float)
> (coerce hi 'double-float)
> (make-marray 'double-float
> :initial-contents
> (coerce (,fun lo hi count) 'list))))
>
> and one example of its use:
>
> (defun useq (lo hi &optional (count 51))
> (dist my-utils:rseq))
>
> The function compilation fails, because the `declare' form is not the
> first form in the function body. It is nested behind `progn'
>
> 1) how to accomplish this with a macro? 2) Or should I use a function
> instead, passing `fun' as an argument?

Looking up declare in the HS [1] tells you where you can use it. In
the original form of the function, I would go for LOCALLY. But note
that the version you submitted doesn't make a whole lot of sense:
coercing lo and hi but not using the values is kind of silly.

And anyway, this should certainly be a function, not a macro. You are
passing LO and HI by variable capture, which is a very, very, very bad
idea.

Finally, declaring something to be NUMBER is unlikely to be a
significant source of improvement in most implementations. I would
just drop that declaration altogether.

You tried to trade a concise and understandable function for an
obfuscated macro that doesn't buy you anything. There is a lesson in
this.

Tamas

[1] file:///usr/share/doc/hyperspec/Body/s_declar.htm

From: Alex Mizrahi on
M> I would like to use a macro to expand into a function body, but it
M> needs to have a declare statement at the top. Here is the macro:

M> (defmacro dist (fun)
M> `(progn
M> (declare (number lo hi))
M> (coerce lo 'double-float)
M> (coerce hi 'double-float)
M> (make-marray 'double-float
M> :initial-contents
M> (coerce (,fun lo hi count) 'list))))

Version which could actually make sense:

(defmacro defdist (name paramlist fun)
`(defun ,name paramlist
(declare (number lo hi))
(make-array 'double-float
:initial-contents (coerce
(,fun (coerce lo 'double-float)
(coerce hi 'double-float)
count)
'list))))

M> and one example of its use:

M> (defun useq (lo hi &optional (count 51))
M> (dist my-utils:rseq))

(defdist useq (lo hi &optional (count 51)) my:utils:rseq)

M> 2) Or should I use a function instead, passing `fun' as an argument?

Well, if you have lots of this, defdist macro might make sense. Otherwise,
function is more appropriate.

From: fortunatus on
On Mar 4, 12:40 pm, Mirko <mirko.vuko...(a)gmail.com> wrote:
> 2) Or should I use a function instead, passing `fun' as an argument?

Such a grey area! Do what comes naturally, baby... For me my
decisions on function/macro have changed a lot as I've worked with
Lisp over the last several years.

Now I basically go down this list for myself:

o If I want to pass in raw segments of code (like making a new
control-flow construct) then I use a macro. If passing a function in
feels natural, then I consider going with a function.

o Is the thing going to be "declarative" - then I also consider
macro.

o Is the thing going to be "operative" - IOW, "do something" actively
- then I try to stick with function.

o Finally, if the thing is operative but rather simple and impacts
performance, like used inside inner loops, consider macro.


(Please don't ask me to define "declarative" and "operative" - the
whole idea for me is to capture a feeling or sense about what I'm
writing... I trust my intuition in that way - later I can argue on a
more engineering oriented basis, and perhaps even change my mind. But
the creative phase engages my emotions as much as my engineering mind,
and my intuition generally plays out well. [IMHO!!])
From: Joshua Taylor on
fortunatus wrote:
> On Mar 4, 12:40 pm, Mirko <mirko.vuko...(a)gmail.com> wrote:
>> 2) Or should I use a function instead, passing `fun' as an argument?
>
> Such a grey area! Do what comes naturally, baby... For me my
> decisions on function/macro have changed a lot as I've worked with
> Lisp over the last several years.
>
> Now I basically go down this list for myself:
>
> o If I want to pass in raw segments of code (like making a new
> control-flow construct) then I use a macro. If passing a function in
> feels natural, then I consider going with a function.
>
> o Is the thing going to be "declarative" - then I also consider
> macro.
>
> o Is the thing going to be "operative" - IOW, "do something" actively
> - then I try to stick with function.
>
> o Finally, if the thing is operative but rather simple and impacts
> performance, like used inside inner loops, consider macro.

This is getting a little bit away from the OP's OQ, but no matter. One
of the techniques I've started using over the past year or two is
writing function-passing versions of things first, and then writing a
macro wrapper that expands into the function-passing version. E.g.,


CL-USER 2 >
(defun invoke-with-successor (number function)
(funcall function (1+ number)))
INVOKE-WITH-SUCCESSOR

CL-USER 3 >
(defmacro with-successor ((successor number) &body body)
`(invoke-with-successor
,number
#'(lambda (,successor)
,@body)))
WITH-SUCCESSOR

CL-USER 4 >
(with-successor (x 45)
(make-list 10 :initial-element x))
(46 46 46 46 46 46 46 46 46 46)


Then you have all the flexibility of the function-passing version and
all the convenience of the macro both available if you need them. I
find it makes bindings a little bit easier (no messing around with
gensyms, &c.), and declares usually end up the right place
automatically. And, though I haven't needed to do this, it seems to me
that if there are any performance issues, a compiler-macro for
invoke-with-successor could be written that would rewrite calls with
literal (lambda () �) and #'(lambda () �) arguments.

This approach isn't quite as useful for macros that want to establish
function bindings, e.g., for local functions, since the functions passed
to invoke-with-� functions would have to FUNCALL or APPLY their
arguments, but in the macro version, we'd really like to get genuine
local functions that can appear as the CAR of a list. This leads to
some local function definition in the macroexpansion that just do a
FUNCALL or APPLY. E.g.,


CL-USER 12 >
(defun invoke-with-multiplier (number function)
(flet ((multiply (x)
(* x number)))
(funcall function #'multiply)))
INVOKE-WITH-MULTIPLIER

CL-USER 13 >
(invoke-with-multiplier 3 #'(lambda (mult) (funcall mult 5)))
15

CL-USER 14 >
(defmacro with-multiplier ((multiplier number)
&body body
&aux (mult (gensym (string '#:mult-))))
`(invoke-with-multiplier ,number
#'(lambda (,mult)
(flet ((,multiplier (x)
(funcall ,mult x)))
,@body))))
WITH-MULTIPLIER

CL-USER 15 >
(with-multiplier (multiply 34)
(multiply 2))
68


It still works, of course, but for function binding constructs, it's not
clear that function-passing and macro-that-bind-new-functions versions
are equally convenient.

//JT
 |  Next  |  Last
Pages: 1 2
Prev: Elephant experiences?
Next: [ANN] ECL 10.3.1