Prev: Elephant experiences?
Next: [ANN] ECL 10.3.1
From: Mirko on 4 Mar 2010 12:40 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 4 Mar 2010 12:49 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 4 Mar 2010 13:25 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 5 Mar 2010 09:45 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 5 Mar 2010 10:23
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 |