[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

Macro help

nandryshak

8/7/2015 4:07:00 PM

Hello,

I'm running into some trouble writing a macro. I want to be able to write
something like this:

(with-progress "Doing some things that take time"
(http-request)
(sleep 2)
(something-that-takes-a-while))

And turn it into this:

(format t "Doing somethings that take time")
(format t "..")
(http-request)
(format t "..")
(sleep 2)
(format t "..")
(something-that-takes-a-while)
(format t "Done.")

Seems straight forward enough. However, I run into problems when I want to
access variables that are declared outside of the macro, because of the way eval
works. For instance something like this:

(let ((name "nick"))
(with-progress "Doing something"
(http-request :content name)))

results in the error "Unbound variable: name"

This is the macro I have written right now:

(defmacro with-progress (message &rest body)
(let ((progress '(princ "..")))
`(progn
(format t "~A" ,message)
(loop for form in ',body
do (prog
(eval ',progress)
(eval form)))
(format t "Done.~%"))))

Any tips?

Thanks,

Nick
5 Answers

Jocelyn Fréchot

8/7/2015 5:12:00 PM

0

On 07/08/2015 18:07, nandryshak@gmail.com wrote:

> This is the macro I have written right now:
>
> (defmacro with-progress (message &rest body)
> (let ((progress '(princ "..")))
> `(progn
> (format t "~A" ,message)
> (loop for form in ',body
> do (prog
> (eval ',progress)
> (eval form)))
> (format t "Done.~%"))))
>
> Any tips?

You donâ??t want your LOOP form to be evaluated at run time to produce
EVAL forms; you want it to produce (say, â??collectâ?) code
at macro expansion time that will be evaluated at compile time.

You almost never use EVAL, especially at run time and/or without
very good reasons.

--
Jocelyn Fréchot

Carlos

8/7/2015 5:13:00 PM

0

On Fri, 7 Aug 2015 09:07:18 -0700 (PDT)
nandryshak@gmail.com wrote:

> Hello,
>
> I'm running into some trouble writing a macro. I want to be able to
> write something like this:
>
> (with-progress "Doing some things that take time"
> (http-request)
> (sleep 2)
> (something-that-takes-a-while))
>
> And turn it into this:
>
> (format t "Doing somethings that take time")
> (format t "..")
> (http-request)
> (format t "..")
> (sleep 2)
> (format t "..")
> (something-that-takes-a-while)
> (format t "Done.")
>

You are complicating it excessively, probably not thinking about the
code as data.

What you want to do is transform a list by inserting a constant element
(princ "..") before each element...

((task1) (task2)) -> ((princ "..") (task1) (princ "..") (task2))

and then insert an element at the
beginning (princ "start") and another at the end (princ "Done")

((princ "start") (print "..") (task1)
(princ "..") (task2) (princ "done"))

Oh, and insert 'progn at the start, to make it a valid Lisp statement.

(progn
(princ "start") (print "..") (task1)
(princ "..") (task2) (princ "done"))

You can write a function to do the first task:

(defun insert-before-each-element (what list)
(mapcan ... ;; or loop, or whatever
...))

Then it becomes easier:

(defmacro with-progress (message &body body)
(append (list* 'progn
`(format t "%a" ,message)
(insert-before-each-element `(format t "..") body))
`(format t "Done.")))

(code not tested)

Hope this helps.
--

nandryshak

8/7/2015 6:22:00 PM

0

Thanks Jocelyn, Carlos!

I ended up with this:

(defmacro with-progress (message &rest body)
(append (list* 'progn
`(format t "~A" ,message)
(loop
for form in `,body
collect '(format t "..")
collect form))
'((format t "Done.~%"))))

which works well.

Nick

Jocelyn Fréchot

8/7/2015 7:12:00 PM

0

On 07/08/2015 20:21, nandryshak@gmail.com wrote:

> I ended up with this:
>
> (defmacro with-progress (message &rest body)
> (append (list* 'progn
> `(format t "~A" ,message)
> (loop
> for form in `,body
> collect '(format t "..")
> collect form))
> '((format t "Done.~%"))))

One can make macro definition a bit clearer with proper use
of backquotes and splicers:

(defmacro with-progress (message &rest body)
`(progn
(format t "~A" ,message)
,@(loop :for form :in body
:collect '(format t "..")
:collect form)
(format t "Done.~%")))


--
Jocelyn Fréchot

Kaz Kylheku

8/7/2015 8:17:00 PM

0

On 2015-08-07, nandryshak@gmail.com <nandryshak@gmail.com> wrote:
> This is the macro I have written right now:
>
> (defmacro with-progress (message &rest body)
> (let ((progress '(princ "..")))
> `(progn
> (format t "~A" ,message)
> (loop for form in ',body
> do (prog
> (eval ',progress)
> (eval form)))
> (format t "Done.~%"))))
>
> Any tips?

The loop has to be executed during macro-expansion, not inserted
into the output! That's all, pretty much.

The loop has to interpose the progress among the forms,
and then the resulting list of forms has to be spliced into
the (progn ...).

The DO clause of LOOP takes multiple forms; no prog needed.

Anyway, we will need a COLLECT clause, or more likely APPEND.

(defmacro with-progress (message &rest body)
(let ((progress '(princ "..")))
`(progn
(format t "~A" ,message)
,@(loop for form in body
appending `(,progress ,form))
(format t "Done.~%"))))

See, the loop is unquoted (unquote spliced) so we just
refer to the body variable as body, not ,body.
The progress/form pair is constructed with backquote again,
and so those variables are referred to using unquoting.
We could replace that little backquote with (list progress form).

A minor correction in the end, as you can see.

To test your macro, use macroexpand first. Is the expansion correct?
If the expansion looks right, only then try running it:

[9]> (macroexpand '(with-progress "foo"))
(PROGN (FORMAT T "~A" "foo") (FORMAT T "Done.~%")) ;
T
[10]> (macroexpand '(with-progress "foo" X))
(PROGN (FORMAT T "~A" "foo") (PRINC "..") X (FORMAT T "Done.~%")) ;
T
[11]> (macroexpand '(with-progress "foo" X Y))
(PROGN (FORMAT T "~A" "foo") (PRINC "..") X (PRINC "..") Y (FORMAT T "Done.~%")) ;
T
[12]> (macroexpand '(with-progress "foo" X Y Z))
(PROGN (FORMAT T "~A" "foo") (PRINC "..") X (PRINC "..") Y (PRINC "..") Z
(FORMAT T "Done.~%")) ;
T