From: Mark Carter
Subject: Macro noob
Date: 
Message-ID: <44185f50$0$15793$14726298@news.sunsite.dk>
I'm trying to write a macro that prints when a body starts and finishes.

Here's what I've got so far:

(defmacro processing (text &rest body)
   (format nil "Start Processing ~A" text)
   body
   (format nil "Finished Processing ~A" text))

(processing "hayho" (write-line "hidee") (write-line "ho"))

What I'm expecting to see is
Start Processing hayho
hidee
ho
Finished Processing hayho

What I actually see is:
Finished Processing hayho

I've tried substituting body with `(progn ,@body)
which seems to be in line with Peter Siebel's alternative definition of:
(defmacro when (condition &rest body)
   `(if ,condition (progn ,@body)))
but it appears that I'm way off base.

From: ·········@gmail.com
Subject: Re: Macro noob
Date: 
Message-ID: <1142448835.893925.69570@v46g2000cwv.googlegroups.com>
Hi!

You do several things a bit wrong. Try using (macroexpand '(processing
"hayho" (write-line "hidee") (write-line "ho"))) to see what your macro
really does. Also, you supply nil to format, try t instead to make it
print the result.

This is what I think you meant:

(defmacro processing (text &rest body)
  `(progn (format t "Start Processing ~A~%" ,text)
	  ,@body
	  (format t "Finished Processing ~A~%" ,text)))

Good luck!
From: Mark Carter
Subject: Re: Macro noob
Date: 
Message-ID: <44186aa1$0$15791$14726298@news.sunsite.dk>
·········@gmail.com wrote:
> Hi!
> 
> You do several things a bit wrong. Try using (macroexpand '(processing
> "hayho" (write-line "hidee") (write-line "ho"))) to see what your macro
> really does. Also, you supply nil to format, try t instead to make it
> print the result.
> 
> This is what I think you meant:
> 
> (defmacro processing (text &rest body)
>   `(progn (format t "Start Processing ~A~%" ,text)
> 	  ,@body
> 	  (format t "Finished Processing ~A~%" ,text)))
> 
> Good luck!

Thanks guys.

Now, let's see if I can get this right ...

I'm taking it that macroexpand-1 is your friend, which can be used to 
"iteratively refine" ones macros (or in my case, taking potshots and 
seeing what works ;) )

The ` creates a list. The contents of the list are not evaluated, except 
when you have a comma, which does. And the @ "flattens" the list, or 
thereabouts.

The golden rule of macros would appear to be: write short ones!

Of course, my macro could probably be improved by making it return the 
result of the last form evaluated by body, which looks like I need 
gensym. I'll investigate.
From: Pascal Costanza
Subject: Re: Macro noob
Date: 
Message-ID: <47r926Fgr8f9U1@individual.net>
Mark Carter wrote:

> The golden rule of macros would appear to be: write short ones!

At least in the beginning. Over time, you will get more confident 
writing more complex macros. That's actually similar to any technique 
that you have to practice before you can master it.

> Of course, my macro could probably be improved by making it return the 
> result of the last form evaluated by body, which looks like I need 
> gensym. I'll investigate.

Check out PROG1.


Pascal

-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Lars Brinkhoff
Subject: Re: Macro noob
Date: 
Message-ID: <85zmjqzu75.fsf@junk.nocrew.org>
Pascal Costanza <··@p-cos.net> writes:
> Mark Carter wrote:
>> Of course, my macro could probably be improved by making it return
>> the result of the last form evaluated by body, which looks like I
>> need gensym. I'll investigate.
> Check out PROG1.

And also MULTIPLE-VALUE-PROG1.
From: Kaz Kylheku
Subject: Re: Macro noob
Date: 
Message-ID: <1142449161.765829.258580@e56g2000cwe.googlegroups.com>
Mark Carter wrote:
> I'm trying to write a macro that prints when a body starts and finishes.
>
> Here's what I've got so far:
>
> (defmacro processing (text &rest body)
>    (format nil "Start Processing ~A" text)

If you use NIL as the stream parameter in FORMAT, it means that the
output goes to a string. The value T means it goes to
*STANDARD-OUTPUT*.

So here, you text is going nowhere. A string is returned, which is
discarded.

>    body
>    (format nil "Finished Processing ~A" text))

This will fail to print the finish message if the body terminates by a
non-local exit. There is a second problem too: your macro fails to
propagate the values returned by the last form. The return value of the
macro expansion will be that of the last FORMAT call.

Consider this:

  (unwind-protect
    (progn
      (format t "Start processing ~a" text)
      ... body ...
      )
    (format t "Finished processing ~a" text))

The UNWIND-PROTECT also takes care of returning the value of the
protected form. The cleanup-form's result values, if any, are ignored.

> What I actually see is:
> Finished Processing hayho

What you are seeing is the return value of the FORMAT NIL. Actually,
you should be seeing that in quotes because it's a string object.
From: Ken Tilton
Subject: Re: Macro noob
Date: 
Message-ID: <u4_Rf.61$G65.3@fe12.lga>
Kaz Kylheku wrote:
> Mark Carter wrote:
> 
>>I'm trying to write a macro that prints when a body starts and finishes.
>>
>>Here's what I've got so far:
>>
>>(defmacro processing (text &rest body)
>>   (format nil "Start Processing ~A" text)
> 
> 
> If you use NIL as the stream parameter in FORMAT, it means that the
> output goes to a string. The value T means it goes to
> *STANDARD-OUTPUT*.
> 
> So here, you text is going nowhere. A string is returned, which is
> discarded.
> 
> 
>>   body
>>   (format nil "Finished Processing ~A" text))
> 
> 
> This will fail to print the finish message if the body terminates by a
> non-local exit.

Actually, that sounds like a feature. I do not want to see a "finished" 
message if I did not finish.

Howsabout:

     `(prog2
         (format...)
         (progn ,@body)
         (format...))

kt

-- 
Cells: http://common-lisp.net/project/cells/

"And I will know my song well before I start singing."  - Bob Dylan
From: Mark Carter
Subject: Re: Macro noob
Date: 
Message-ID: <441878ab$0$15782$14726298@news.sunsite.dk>
Ken Tilton wrote:

> Howsabout:
> 
>     `(prog2
>         (format...)
>         (progn ,@body)
>         (format...))

I see! Indeed, I wasn't even aware of the existence of such things as 
prog1, prog2 and unwind-protect before I read this thread. So many 
thanks to all respondents. I'm going to write this up and put it on my 
website so I can remember it.

I had been playing about with some other stuff, too, to do with the 
processing of files. I tried several ways of doing it, including a 
"functional" approach like so:

(defun process-file (filename initial-state process-line)
   "process a file using function process-line
"
   (with-open-file (in filename)
     (let ((state initial-state))
       (loop for line = (read-line in nil nil)
	    while line do
	    (setf state (funcall process-line state line)))
       state)))

I don't know if anyone else agrees with me, but I think that one problem 
with this approach is that it's OK if the state you're trying to store 
is simple, but it leads to burdensome packing and unpacking if there's 
"lots" of "different types of" state, if you can see what I mean. An 
approach using macros would seem to "flatten out" the processing you do 
... as opposed to a more functional approach, where you pass the whole 
world in, and the whole world out, as it were.

... in the end, though, I decided on simply slurping the file. The Lisp 
I was using (Corman) didn't seem to have any problems with it. You also 
have the advantage of setting it to a variable. I slurped in a 28MB 
file. When I save the image, it packs down to 5MB (a feat which seems 
puzzling to achieve unless some kind of compression is being used in the 
background). I'm not sure if I'm too keen on the image-based approach, 
though, as it reminds me too much of Excel; and you wouldn't believe how 
wonky that can be. No wait. You probably can.
From: Frank Buss
Subject: Re: Macro noob
Date: 
Message-ID: <1tjyyj5ez75mt.gxq4vzh3zda8$.dlg@40tude.net>
Mark Carter wrote:

> (defun process-file (filename initial-state process-line)
>    "process a file using function process-line
> "
>    (with-open-file (in filename)
>      (let ((state initial-state))
>        (loop for line = (read-line in nil nil)
> 	    while line do
> 	    (setf state (funcall process-line state line)))
>        state)))
> 
> I don't know if anyone else agrees with me, but I think that one problem 
> with this approach is that it's OK if the state you're trying to store 
> is simple, but it leads to burdensome packing and unpacking if there's 
> "lots" of "different types of" state, if you can see what I mean.

A more elegant solution without macros (a solution with macros would look
like "with-open-file") is to use closures:

(defun process-file (filename process-line)
  "process a file using function process-line"
  (with-open-file (in filename)
    (loop for line = (read-line in nil nil) while line
          do (funcall process-line line))))

(defun count-words (string)
  "returns the number of words in a string"
  (loop for char across string
        with count = 0 and in-word = nil
        finally (return count)
        do (if (alphanumericp char)
               (unless in-word
                 (setf in-word t)
                 (incf count))
             (setf in-word nil))))

(defun file-statistic (filename)
  "prints the number of lines and words in a file"
  (let ((lines 0)
        (words 0))
    (process-file filename #'(lambda (line)
                               (incf lines)
                               (incf words (count-words line))))
    (format t "The file ~a has ~a words in ~a lines~%"
            filename
            words
            lines)))


CL-USER > (file-statistic "c:\\tmp\\test.txt")
The file c:\tmp\test.txt has 24 words in 6 lines

You can build really interesting and easy to use systems with higher order
functions:

http://www.frank-buss.de/lisp/functional.html

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Deon Garrett
Subject: Re: Macro noob
Date: 
Message-ID: <87acbr4iny.fsf@clapton.csm.astate.edu>
Mark Carter <··@privacy.net> writes:

> I'm trying to write a macro that prints when a body starts and finishes.
>
> Here's what I've got so far:
>
> (defmacro processing (text &rest body)
>   (format nil "Start Processing ~A" text)
>   body
>   (format nil "Finished Processing ~A" text))
>
> (processing "hayho" (write-line "hidee") (write-line "ho"))
>
> What I'm expecting to see is
> Start Processing hayho
> hidee
> ho
> Finished Processing hayho
>
> What I actually see is:
> Finished Processing hayho

Think of macros as functions that are run by the compiler/interpreter and
that return code that will be then evaluated.  In your case, when you
evaluate at the REPL the expression 

(processing "hayho" (write-line "hidee") (write-line "ho"))

the macro is expanded according to the definition.  The compiler runs the
macro and the result is then evaluated by the REPL.  So what does your
macro return?  Your macro simply returns the value of the last expression
evaluated -- in this case (format nil "Finished Processing ~A" text).
When the second argument to format is nil, the string is not printed, but
rather returned silently.  But because the REPL automatically prints the
value of the expression just evaluated, you see that return value.  To
actually make the output appear on screen, you want to pass t to format in
place of the nil, for example (format t "hello").

Now the bigger problem.  The only code that the REPL actually sees is the
return value of the macro, which in your version is simply the return
value of the final expression.  You need to arrange for the macro to
return the *code* that you want evaluated.  macroexpand-1 is your friend.
It will show you what the macro returns, which is what the REPL will
evaluate.

[2]> (macroexpand-1 '(processing "hayho" (write-line "hidee") (write-line "ho")))
"Finished Processing hayho" ;
T

Notice the return value is the literal string "Finished Processing hayho",
which would then be evaluated by the REPL yielding itself.  Hence, all you
see is that string.

You need to return something like this:

(progn (format t "Start ~a~%" "hayho")
       (write-line "hidee")
       (write-line "ho")
       (format t "End ~a~%" "hayho"))

Notice this is just a list.  So we can rewrite the macro to build up and
return this list like so.

(defmacro processing (text &body body)
  (list 'progn (list 'format 't "Start ~a~%" text)
               body
               (list 'format 't "End ~a~%" text)))

Notice that lots of things are quoted here, but text and body are not.
This is because I want the *values* of these variables and quoting them
would yield the symbol names themselves.  This comes closer, but still
doesn't quite work, because the body will have too many parenthesis.
Using macroexpand-1 again would show something like this for middle of the
expansion...

((write-line "hidee") (write-line "ho"))

which is invalid because (write-line "hidee") is the car of the list and
is not a function name as required.  What we need is for the individual
forms in the body parameter to be spliced into place without the
surrounding parenthesis.

Aside from that, it's also hideous to write and look at that macro.  Both
these problems are solved with the backquote.  Backquote is like quote,
but any quoted form can be "unquoted" by preceding it with a comma (or ,@
to splice a list into the form in place).

Using the backquote, we can get something like this

(defmacro processing (text &body body)
  `(progn (format t "Starting ~a~%" ,text)
          ,@body
          (format t "Endign ~a~%" ,text)))

Note the backquote preceding the progn and the commas preceding "text" and
the ,@ preceding body.  Instead of quoting almost everything as in the
previous version, I simply quote the whole progn form, but use the comma
operator to "unquote" the forms I want to be evaluated during the
expansion of the macro.

> I've tried substituting body with `(progn ,@body)
> which seems to be in line with Peter Siebel's alternative definition of:
> (defmacro when (condition &rest body)
>   `(if ,condition (progn ,@body)))
> but it appears that I'm way off base.

The thing that you must understand about macros is when they are run.
When you type a call to a macro at the REPL, the macro is ran first, and
its return value is evaluated by the REPL.  With a function, the form
itself is evaluated, and only the return value is printed.  Just keep
repeating the mantra, "Macros return code.  Macros return code."

-- 
Deon Garrett
Institute for Intelligent Systems
The University of Memphis
·········@gmail.com
From: Frank Buss
Subject: Re: Macro noob
Date: 
Message-ID: <10o15i8739e7e$.174royrrlq2yi.dlg@40tude.net>
Mark Carter wrote:

> I'm trying to write a macro that prints when a body starts and finishes.
> 
> Here's what I've got so far:
> 
> (defmacro processing (text &rest body)
>    (format nil "Start Processing ~A" text)
>    body
>    (format nil "Finished Processing ~A" text))
> 
> (processing "hayho" (write-line "hidee") (write-line "ho"))
> 
> What I'm expecting to see is
> Start Processing hayho
> hidee
> ho
> Finished Processing hayho

a macro returns the last form and "format nil" returns a string instead of
writing it to stdout. To match your expected output, try this:

(defmacro processing (text &rest body)
  `(progn
     (format t "Start Processing ~A~%" ,text)
     ,@body
     (format t "Finished Processing ~A~%" ,text)))

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de