From: ···········@gmail.com
Subject: changing text color
Date: 
Message-ID: <cc40288f-0a5e-4e50-8fd0-6a760a378a6d@e10g2000prf.googlegroups.com>
HI.This my first post here. I would like to change the color of the
text ,so when I use princ or print or format I see it in the colored i
have already specified.

thank you

From: vanekl
Subject: Re: changing text color
Date: 
Message-ID: <f55f16ba-3f78-488d-b86d-21a7709fefe1@p69g2000hsa.googlegroups.com>
On Jan 1, 10:05 pm, ···········@gmail.com wrote:
> HI.This my first post here. I would like to change the color of the
> text ,so when I use princ or print or format I see it in the colored i
> have already specified.
>
> thank you

This works with the color xterm I use:

(defmacro cmsg (fmt &optional (c 33) (skip 0) (stream t))
  `(let ((color ,c))
    (if (stringp color)
    (setf color (cond ((equalp "Black"   color)  30)
              ((equalp "Red"     color)  31)
              ((equalp "Green"   color)  32)
              ((equalp "Yellow"  color)  33)
              ((equalp "Blue"    color)  34)
              ((equalp "Magenta" color)  35)
              ((equalp "Cyan"    color)  36)
              ((equalp "White"   color)  37)
              (t 0))))

    (let ((fmts (format nil "~A[0;~Dm" (code-char #x1b)  color))
          (fmte (format nil "~A[0m"    (code-char #x1b))))

      (if ,skip
      (dotimes (i ,skip)
        (format ,stream "~%")))
        ; Should use princ here instead of format because function
'format'
        ; sometimes gets confused when it thinks it sees control codes
        ; mixed in with the format statement and no arguments are
        ; supplied for the control codes.
      (princ fmts ,stream)
      (princ ,fmt ,stream)
      ;(princ (concatenate 'string fmts ,fmt fmte) ,stream)
      ;warning: sbcl may insert warning messages here if there is a CR
in "fmt".
      (princ fmte ,stream)
      )))
From: ···········@gmail.com
Subject: Re: changing text color
Date: 
Message-ID: <992a79fc-965a-44d5-8426-b7d15340338b@s19g2000prg.googlegroups.com>
On 1 jan, 23:47, vanekl <·····@acd.net> wrote:
> On Jan 1, 10:05 pm, ···········@gmail.com wrote:
>
> > HI.This my first post here. I would like to change the color of the
> > text ,so when I use princ or print or format I see it in the colored i
> > have already specified.
>
> > thank you
>
> This works with the color xterm I use:
>
> (defmacro cmsg (fmt &optional (c 33) (skip 0) (stream t))
>   `(let ((color ,c))
>     (if (stringp color)
>     (setf color (cond ((equalp "Black"   color)  30)
>               ((equalp "Red"     color)  31)
>               ((equalp "Green"   color)  32)
>               ((equalp "Yellow"  color)  33)
>               ((equalp "Blue"    color)  34)
>               ((equalp "Magenta" color)  35)
>               ((equalp "Cyan"    color)  36)
>               ((equalp "White"   color)  37)
>               (t 0))))
>
>     (let ((fmts (format nil "~A[0;~Dm" (code-char #x1b)  color))
>           (fmte (format nil "~A[0m"    (code-char #x1b))))
>
>       (if ,skip
>       (dotimes (i ,skip)
>         (format ,stream "~%")))
>         ; Should use princ here instead of format because function
> 'format'
>         ; sometimes gets confused when it thinks it sees control codes
>         ; mixed in with the format statement and no arguments are
>         ; supplied for the control codes.
>       (princ fmts ,stream)
>       (princ ,fmt ,stream)
>       ;(princ (concatenate 'string fmts ,fmt fmte) ,stream)
>       ;warning: sbcl may insert warning messages here if there is a CR
> in "fmt".
>       (princ fmte ,stream)
>       )))

Thank you. If i would like to write in green , how should I call the
function?
From: vanekl
Subject: Re: changing text color
Date: 
Message-ID: <f27c2a3a-53c9-4272-a283-ca5783b6064f@d21g2000prf.googlegroups.com>
On Jan 2, 10:50 am, ···········@gmail.com wrote:
> On 1 jan, 23:47, vanekl <·····@acd.net> wrote:
>
>
>
> > On Jan 1, 10:05 pm, ···········@gmail.com wrote:
>
> > > HI.This my first post here. I would like to change the color of the
> > > text ,so when I use princ or print or format I see it in the colored i
> > > have already specified.
>
> > > thank you
>
> > This works with the color xterm I use:
>
> > (defmacro cmsg (fmt &optional (c 33) (skip 0) (stream t))
> >   `(let ((color ,c))
> >     (if (stringp color)
> >     (setf color (cond ((equalp "Black"   color)  30)
> >               ((equalp "Red"     color)  31)
> >               ((equalp "Green"   color)  32)
> >               ((equalp "Yellow"  color)  33)
> >               ((equalp "Blue"    color)  34)
> >               ((equalp "Magenta" color)  35)
> >               ((equalp "Cyan"    color)  36)
> >               ((equalp "White"   color)  37)
> >               (t 0))))
>
> >     (let ((fmts (format nil "~A[0;~Dm" (code-char #x1b)  color))
> >           (fmte (format nil "~A[0m"    (code-char #x1b))))
>
> >       (if ,skip
> >       (dotimes (i ,skip)
> >         (format ,stream "~%")))
> >         ; Should use princ here instead of format because function
> > 'format'
> >         ; sometimes gets confused when it thinks it sees control codes
> >         ; mixed in with the format statement and no arguments are
> >         ; supplied for the control codes.
> >       (princ fmts ,stream)
> >       (princ ,fmt ,stream)
> >       ;(princ (concatenate 'string fmts ,fmt fmte) ,stream)
> >       ;warning: sbcl may insert warning messages here if there is a CR
> > in "fmt".
> >       (princ fmte ,stream)
> >       )))
>
> Thank you. If i would like to write in green , how should I call the
> function?

(with-open-file (stream #P"/tmp/t.txt" :direction :output :if-
exists :supersede)
  (cmsg "hi" "Green" 1 stream))
From: mathrick
Subject: Re: changing text color
Date: 
Message-ID: <f951841a-8c54-4cbb-9d6f-8d6b89854eb3@h11g2000prf.googlegroups.com>
On Jan 1, 11:47 pm, vanekl <·····@acd.net> wrote:
> This works with the color xterm I use:
>
> (defmacro cmsg (fmt &optional (c 33) (skip 0) (stream t))
>   `(let ((color ,c))

*BZZZZZT* You just captured COLOR here. Which has a very good chance
of being bound in code that concerns itself with printing something in
colour. You *must* use WITH-GENSYMS or equivalent to avoid doing that,
else there will be very nasty and hard to debug problems at seemingly
random moments.

Consider this:

CL-USER> (defun print-warning (text &optional (color "red"))
           (cmsg text)
           (format t "~&~%;; Printed warning message in ~A" color))

; in: lambda nil
;     (CMSG TEXT)
[snip]
;
; caught warning:
;   undefined variable: in
;
; caught warning:
;   undefined variable: princ

;
; caught warning:
;   These variables are undefined:
;     in princ

CL-USER> (print-warning "NUCLEAR STRIKE APPROACHING")
NUCLEAR STRIKE APPROACHING

=============================
The variable in is unbound.
   [Condition of type unbound-variable]

Restarts:
 0: [abort] Return to SLIME's top level.
 1: [terminate-thread] Terminate this thread (#<thread "repl-
thread" {AFDA9A1}>)

Backtrace:
  0: (print-warning "NUCLEAR STRIKE APPROACHING" "red")
  ---...---

>     (if (stringp color)
>     (setf color (cond ((equalp "Black"   color)  30)
>               ((equalp "Red"     color)  31)
>               ((equalp "Green"   color)  32)
>               ((equalp "Yellow"  color)  33)
>               ((equalp "Blue"    color)  34)
>               ((equalp "Magenta" color)  35)
>               ((equalp "Cyan"    color)  36)
>               ((equalp "White"   color)  37)
>               (t 0))))

This is long enough a COND to warrant writing CASE* for it.

>     (let ((fmts (format nil "~A[0;~Dm" (code-char #x1b)  color))
>           (fmte (format nil "~A[0m"    (code-char #x1b))))
>
>       (if ,skip
>       (dotimes (i ,skip)
>         (format ,stream "~%")))

What exactly is SKIP? You never explain it, and I assumed it meant
"don't colour SKIP first chars", but that's seemingly not the case.
You need to document parameters like that better, perhaps by naming it
SKIP-LINES.

>         ; Should use princ here instead of format because function
> 'format'
>         ; sometimes gets confused when it thinks it sees control codes
>         ; mixed in with the format statement and no arguments are
>         ; supplied for the control codes.
>       (princ fmts ,stream)
>       (princ ,fmt ,stream)
>       ;(princ (concatenate 'string fmts ,fmt fmte) ,stream)
>       ;warning: sbcl may insert warning messages here if there is a CR
> in "fmt".
>       (princ fmte ,stream)
>       )))

In general, your naming leaves a lot to be desired. FMT, and C are not
good names for public API parameters. Consider someone trying to
decipher what a "cmsg" does looking at SLIME-provided arglist. C and
FMT tell _nothing_. Same with FMTS and FMTE. They're not packed in
long lines where brievity could matter, they're the sole params used
on separate lines. OTOH, PRINCing something in sequence is not
immediately obvious, so naming them better makes the code much
clearer.

Additionally, FMT suggests your macro takes FORMAT-style formatting
directives, which it doesn't. So you're confusing people even further
with bad naming.

Here's a version that fixes the problems I outlined:

;;; Just the usual stuff. Everyone should have a WITH-GENSYMS or
;;; WITH-UNIQUE-NAMES in their toolbox.
(defmacro with-gensyms ((&rest names) &body body)
  `(let (,@(loop for name in names collect `(,name (gensym))))
     ,@body))

(defmacro case* (key-form-or-list &body cases)
  "Like CASE, but lets you specify an optional equality predicate:
  (case (foo 'equal)
    (\"asd\" (bar)))"
  (if (listp key-form-or-list)
      (destructuring-bind (keyform &optional (test ''eql)) key-form-or-
list
        (let ((keyval (gensym)))
          (flet ((make-case (casedef)
                   (if (eq t (car casedef))
                       casedef
                       `((funcall ,test ,keyval ,(car casedef))
                         ,@(cdr casedef)))))
            `(let ((,keyval ,keyform))
               (cond
                 ,@(loop for case in cases collecting (make-case
case)))))))
      `(case ,key-form-or-list ,@cases)))

(defmacro cmsg (message &optional (colour 33) (skip-lines 0) (stream
t))
  (with-gensyms (col)
   `(let ((,col ,colour))
      (if (stringp ,col)
          (setf ,col (case* (,col 'equalp)
                       ("Black"   30)
                       ("Red"     31)
                       ("Green"   32)
                       ("Yellow"  33)
                       ("Blue"    34)
                       ("Magenta" 35)
                       ("Cyan"    36)
                       ("White"   37)
                       (t 0))))

      (let ((format-start (format nil "~A[0;~Dm" (code-char
#x1b) ,col))
            (format-end (format nil "~A[0m"    (code-char #x1b))))

        (if ,skip-lines
            (dotimes (i ,skip-lines)
              (format ,stream "~%")))
        ;; Should use princ here instead of format because function
        ;; 'format' sometimes gets confused when it thinks it sees
        ;; control codes mixed in with the format statement and no
        ;; arguments are supplied for the control codes.
        (princ format-start ,stream)
        (princ ,message ,stream)
        ;; (princ (concatenate 'string formats ,format formate)
        ;; ,stream) warning: sbcl may insert warning messages here if
        ;; there is a CR in "format".
        (princ format-end ,stream)))))

Oh, and last thing: why exactly did you make it a macro? There's
absolutely nothing about CMSG that would need macros. It'd be at least
understandable if you wanted to shift the string computation to
compile time and only returned static strings. But you don't do that.
The first rule of writing macros is not to write macros where a
function will do.

Cheers,
Maciej
From: vanekl
Subject: Re: changing text color
Date: 
Message-ID: <72e80295-8d86-4898-aabc-3b012c6c3352@m77g2000hsc.googlegroups.com>
On Jan 5, 9:17 am, mathrick <········@gmail.com> wrote:
> On Jan 1, 11:47 pm, vanekl <·····@acd.net> wrote:
>
> > This works with the color xterm I use:
>
> > (defmacro cmsg (fmt &optional (c 33) (skip 0) (stream t))
> >   `(let ((color ,c))
>
> *BZZZZZT* You just captured COLOR here. Which has a very good chance
> of being bound in code that concerns itself with printing something in
> colour. You *must* use WITH-GENSYMS or equivalent to avoid doing that,
> else there will be very nasty and hard to debug problems at seemingly
> random moments.

Good call.


> Consider this:
>
> CL-USER> (defun print-warning (text &optional (color "red"))
>            (cmsg text)
>            (format t "~&~%;; Printed warning message in ~A" color))
>
> ; in: lambda nil
> ;     (CMSG TEXT)
> [snip]
> ;
> ; caught warning:
> ;   undefined variable: in
> ;
> ; caught warning:
> ;   undefined variable: princ
>
> ;
> ; caught warning:
> ;   These variables are undefined:
> ;     in princ
>
> CL-USER> (print-warning "NUCLEAR STRIKE APPROACHING")
> NUCLEAR STRIKE APPROACHING
>
> =============================
> The variable in is unbound.
>    [Condition of type unbound-variable]
>
> Restarts:
>  0: [abort] Return to SLIME's top level.
>  1: [terminate-thread] Terminate this thread (#<thread "repl-
> thread" {AFDA9A1}>)
>
> Backtrace:
>   0: (print-warning "NUCLEAR STRIKE APPROACHING" "red")
>   ---...---
>
> >     (if (stringp color)
> >     (setf color (cond ((equalp "Black"   color)  30)
> >               ((equalp "Red"     color)  31)
> >               ((equalp "Green"   color)  32)
> >               ((equalp "Yellow"  color)  33)
> >               ((equalp "Blue"    color)  34)
> >               ((equalp "Magenta" color)  35)
> >               ((equalp "Cyan"    color)  36)
> >               ((equalp "White"   color)  37)
> >               (t 0))))
>
> This is long enough a COND to warrant writing CASE* for it.

I just stumbled upon defenumeration.lisp the other day, and that looks
to be even more efficient.


> >     (let ((fmts (format nil "~A[0;~Dm" (code-char #x1b)  color))
> >           (fmte (format nil "~A[0m"    (code-char #x1b))))
>
> >       (if ,skip
> >       (dotimes (i ,skip)
> >         (format ,stream "~%")))
>
> What exactly is SKIP? You never explain it, and I assumed it meant
> "don't colour SKIP first chars", but that's seemingly not the case.
> You need to document parameters like that better, perhaps by naming it
> SKIP-LINES.

I didn't intend this func to be used by anybody but myself when I
wrote it, thus explaining the naming convention/documentation (or lack
thereof).



> >         ; Should use princ here instead of format because function
> > 'format'
> >         ; sometimes gets confused when it thinks it sees control codes
> >         ; mixed in with the format statement and no arguments are
> >         ; supplied for the control codes.
> >       (princ fmts ,stream)
> >       (princ ,fmt ,stream)
> >       ;(princ (concatenate 'string fmts ,fmt fmte) ,stream)
> >       ;warning: sbcl may insert warning messages here if there is a CR
> > in "fmt".
> >       (princ fmte ,stream)
> >       )))
>
> In general, your naming leaves a lot to be desired. FMT, and C are not
> good names for public API parameters. Consider someone trying to
> decipher what a "cmsg" does looking at SLIME-provided arglist. C and
> FMT tell _nothing_. Same with FMTS and FMTE. They're not packed in
> long lines where brievity could matter, they're the sole params used
> on separate lines. OTOH, PRINCing something in sequence is not
> immediately obvious, so naming them better makes the code much
> clearer.
>
> Additionally, FMT suggests your macro takes FORMAT-style formatting
> directives, which it doesn't. So you're confusing people even further
> with bad naming.
>
> Here's a version that fixes the problems I outlined:
>
> ;;; Just the usual stuff. Everyone should have a WITH-GENSYMS or
> ;;; WITH-UNIQUE-NAMES in their toolbox.
> (defmacro with-gensyms ((&rest names) &body body)
>   `(let (,@(loop for name in names collect `(,name (gensym))))
>      ,@body))
>
> (defmacro case* (key-form-or-list &body cases)
>   "Like CASE, but lets you specify an optional equality predicate:
>   (case (foo 'equal)
>     (\"asd\" (bar)))"
>   (if (listp key-form-or-list)
>       (destructuring-bind (keyform &optional (test ''eql)) key-form-or-
> list
>         (let ((keyval (gensym)))
>           (flet ((make-case (casedef)
>                    (if (eq t (car casedef))
>                        casedef
>                        `((funcall ,test ,keyval ,(car casedef))
>                          ,@(cdr casedef)))))
>             `(let ((,keyval ,keyform))
>                (cond
>                  ,@(loop for case in cases collecting (make-case
> case)))))))
>       `(case ,key-form-or-list ,@cases)))
>
> (defmacro cmsg (message &optional (colour 33) (skip-lines 0) (stream
> t))
>   (with-gensyms (col)
>    `(let ((,col ,colour))
>       (if (stringp ,col)
>           (setf ,col (case* (,col 'equalp)
>                        ("Black"   30)
>                        ("Red"     31)
>                        ("Green"   32)
>                        ("Yellow"  33)
>                        ("Blue"    34)
>                        ("Magenta" 35)
>                        ("Cyan"    36)
>                        ("White"   37)
>                        (t 0))))
>
>       (let ((format-start (format nil "~A[0;~Dm" (code-char
> #x1b) ,col))
>             (format-end (format nil "~A[0m"    (code-char #x1b))))
>
>         (if ,skip-lines
>             (dotimes (i ,skip-lines)
>               (format ,stream "~%")))
>         ;; Should use princ here instead of format because function
>         ;; 'format' sometimes gets confused when it thinks it sees
>         ;; control codes mixed in with the format statement and no
>         ;; arguments are supplied for the control codes.
>         (princ format-start ,stream)
>         (princ ,message ,stream)
>         ;; (princ (concatenate 'string formats ,format formate)
>         ;; ,stream) warning: sbcl may insert warning messages here if
>         ;; there is a CR in "format".
>         (princ format-end ,stream)))))
>
> Oh, and last thing: why exactly did you make it a macro? There's
> absolutely nothing about CMSG that would need macros. It'd be at least
> understandable if you wanted to shift the string computation to
> compile time and only returned static strings. But you don't do that.
> The first rule of writing macros is not to write macros where a
> function will do.

I got errors when I first wrote this as a plain function (don't
remember what they were). When I turned it into a macro the errors
went away, so that's the way it stayed. I aint a Lisp wizard, nor do I
claim to be.


>
> Cheers,
> Maciej

Thanks for the tips.

This was meant to be used only by me, so no time was spent on cleaning
up the api. Just trying to give the OP something to work with.
From: Pascal Bourguignon
Subject: Re: changing text color
Date: 
Message-ID: <874pdraop8.fsf@thalassa.informatimago.com>
vanekl <·····@acd.net> writes:
> [...]
> I got errors when I first wrote this as a plain function (don't
> remember what they were). When I turned it into a macro the errors
> went away, so that's the way it stayed. I aint a Lisp wizard, nor do I
> claim to be.

And you won't ever be, if you don't try to understand what you're
doing and why there are errors.

-- 
__Pascal_Bourguignon__               _  Software patents are endangering
()  ASCII ribbon against html email (o_ the computer industry all around
/\  1962:DO20I=1.100                //\ the world http://lpf.ai.mit.edu/
    2001:my($f)=`fortune`;          V_/   http://petition.eurolinux.org/
From: Madhu
Subject: Re: changing text color
Date: 
Message-ID: <m3d4sfjuwn.fsf@robolove.meer.net>
* mathrick in <····································@h11g2000prf.googlegroups.com> Wrote on Sat, 5 Jan 2008 01:17:38 -0800 (PST):

| On Jan 1, 11:47 pm, vanekl <·····@acd.net> wrote:
|> This works with the color xterm I use:
|>
|> (defmacro cmsg (fmt &optional (c 33) (skip 0) (stream t))
|>   `(let ((color ,c))
|
| *BZZZZZT* You just captured COLOR here. Which has a very good chance
| of being bound in code that concerns itself with printing something in
| colour. 

Is this really the problem in this case?  CMESG rebinds COLOR and uses
its own binding of COLOR in its body.  CMESG does *not* expand its
arguments to produce code where COLOR could be rebound. 

(macroexpand-1 '(cmesg "text")) does not show any capture of COLOR.

| You *must* use WITH-GENSYMS or equivalent to avoid doing that,
| else there will be very nasty and hard to debug problems at seemingly
| random moments.

Wanted to point out that in cases where you are not rewriting code,
WITH-GENSYMS is not *essential*. In some places where capture is
possible, one could use ..COLOR.. etc without any great loss.  I
understand this may be hard to digest for someone indoctrinated in
scheme hygiene. :)

I think you may be misinterpreting the results you posted in the rest of
your article
<snip>

--
Madhu
From: Maciej Katafiasz
Subject: Re: changing text color
Date: 
Message-ID: <345da639-84c3-45d7-8b68-82d60d7ebb7a@v29g2000hsf.googlegroups.com>
On Jan 6, 8:05 am, Madhu <·······@meer.net> wrote:
>
> | On Jan 1, 11:47 pm, vanekl <·····@acd.net> wrote:
> |> This works with the color xterm I use:
> |>
> |> (defmacro cmsg (fmt &optional (c 33) (skip 0) (stream t))
> |>   `(let ((color ,c))
> |
> | *BZZZZZT* You just captured COLOR here. Which has a very good chance
> | of being bound in code that concerns itself with printing something in
> | colour.
>
> Is this really the problem in this case?  CMESG rebinds COLOR and uses
> its own binding of COLOR in its body.  CMESG does *not* expand its
> arguments to produce code where COLOR could be rebound.
>
> (macroexpand-1 '(cmesg "text")) does not show any capture of COLOR.

The fact you don't see any doesn't mean there can't be any.

> | You *must* use WITH-GENSYMS or equivalent to avoid doing that,
> | else there will be very nasty and hard to debug problems at seemingly
> | random moments.
>
> Wanted to point out that in cases where you are not rewriting code,
> WITH-GENSYMS is not *essential*. In some places where capture is
> possible, one could use ..COLOR.. etc without any great loss.

What is ..COLOR.., and how do you define "great loss"? Do potential,
extremely hard to track bugs cut it?

> I understand this may be hard to digest for someone indoctrinated in
> scheme hygiene. :)

I don't see what you're basing your indoctrination claims on. I don't
even know Scheme. Macro hygiene is important, and it's always better
to have one too many than one too few safeguard, for the same reason
that writing if branches without {} (in C) is bad and dangerous style
-- you simply never know how the code will be altered, and how it'll
be used. You say the macro isn't rewriting code, and that the capture
can't be a problem? Take a look at this then:

(defun print-warning (text &optional (color "red"))
  (cmsg ((lambda ()
           (format nil "~A~%;; Printed warning in ~A" text color)))
        color))

CL-USER> (print-warning "NUCLEAR STRIKE APPROACHING" "red")
NUCLEAR STRIKE APPROACHING
;; Printed warning in 31

Where does "code rewriting" end? Every macro rewrites code, by the
very definition of what macros do. Sure, it's convoluted code above,
but that's exactly why you want to make sure your macro won't fail,
because one day someone *will* write code that will need that, and
then the fact it fails only in the hard case, and not in easy ones,
will make debugging extra super difficult.

There's no such thing as "too much hygiene", both for a doctor and for
a programmer. Promising functional interface ("not rewriting code")
and then failing to provide that without so much as a notice is a
severe case of malpractice.

> I think you may be misinterpreting the results you posted in the rest of
> your article

Indeed, it was caused by me juggling around several definitions of
CMSG, one of which I mistakenly failed to un-wrap after pasting, which
caused some comments to spill over into code. But my points remain
valid, as shown above.

Cheers,
Maciej
From: Madhu
Subject: Re: changing text color
Date: 
Message-ID: <m3sl1ai1ps.fsf@robolove.meer.net>
* Maciej Katafiasz Wrote on Sun, 6 Jan 2008 10:21:52 -0800 (PST):

|> Wanted to point out that in cases where you are not rewriting code,
|> WITH-GENSYMS is not *essential*. In some places where capture is
|> possible, one could use ..COLOR.. etc without any great loss.
|
| What is ..COLOR.., and how do you define "great loss"? Do potential,
| extremely hard to track bugs cut it?

I cited ..COLOR.. as an example of the tradition to use variable names
such as ".COLOR." where such capture might be possible.

The rest is subjective opinion. The answer to your (rhetorical question)
is of course "yes" :)

|> I understand this may be hard to digest for someone indoctrinated in
|> scheme hygiene. :)
|
| I don't see what you're basing your indoctrination claims on. I don't
| even know Scheme. Macro hygiene is important, and it's always better
| to have one too many than one too few safeguard, for the same reason
| that writing if branches without {} (in C) is bad and dangerous style
| -- you simply never know how the code will be altered, and how it'll
| be used. You say the macro isn't rewriting code, and that the capture
| can't be a problem? Take a look at this then:
|
| (defun print-warning (text &optional (color "red"))
|   (cmsg ((lambda ()
|            (format nil "~A~%;; Printed warning in ~A" text color)))
|         color))
|
| CL-USER> (print-warning "NUCLEAR STRIKE APPROACHING" "red")
| NUCLEAR STRIKE APPROACHING
| ;; Printed warning in 31

This is a valid counter-example, because the proof-by-adversary will
always work in such cases where referential transparency has been
violated.  i.e. Given any such example You can always construct a
counter example.

| Where does "code rewriting" end? Every macro rewrites code, by the
| very definition of what macros do. Sure, it's convoluted code above,
| but that's exactly why you want to make sure your macro won't fail,
| because one day someone *will* write code that will need that, and
| then the fact it fails only in the hard case, and not in easy ones,
| will make debugging extra super difficult.
|
| There's no such thing as "too much hygiene", both for a doctor and for
| a programmer. Promising functional interface ("not rewriting code")
| and then failing to provide that without so much as a notice is a
| severe case of malpractice.

No need to wax eloquent :). Your basic point is of course valid, my
intent was to caution against going overboard with it.

It boils down to the fact that you are the author of the code, and you
can specify the conditions under which it is valid.  This CMSG example
has too many flaws to demonstrate my point, but it should be clear to
you when gensyms are required and when they are not required --- they
are not required when there is no capture possible.  Just following a
rule of the thumb like "ALWAYS USE GENSYMS" doesn't help write clearer
code, and that believing is the sign of indoctrination.

--
Madhu
From: Kent M Pitman
Subject: Re: changing text color
Date: 
Message-ID: <u3ata3v7e.fsf@nhplace.com>
Madhu <·······@meer.net> writes:

> * Maciej Katafiasz Wrote on Sun, 6 Jan 2008 10:21:52 -0800 (PST):
> 
> |> Wanted to point out that in cases where you are not rewriting code,
> |> WITH-GENSYMS is not *essential*. In some places where capture is
> |> possible, one could use ..COLOR.. etc without any great loss.
> |
> | What is ..COLOR.., and how do you define "great loss"? Do potential,
> | extremely hard to track bugs cut it?
> 
> I cited ..COLOR.. as an example of the tradition to use variable names
> such as ".COLOR." where such capture might be possible.

Actually, the .foo. tradition that I've seen is specifically reserved for
the case of variables to be introduced "benignly" in a macro expansion,
where capture is not possible not due to lexical scoping but due
to some combination of packaging and lexical scoping.  (If it's only
lexical scoping protecting you, no special naming convention is needed.)
e.g., if I write:

 (in-package "MY-PACKAGE")

 (defmacro my-prog1 (&rest forms)
   `(let ((.result. ,(first forms)))
      ,@(rest forms)
      .result.))

In such cases, 
 (a) .result. cannot be interfered with in any way by macros 
     in the given forms.
 (b) the only capture that can happen is those using my-package::.result.,
     which is not anyone's business to be using except me, and which
     I can voluntarily opt not to use. [Yes, someone can do

     (my-prog1 3 (print (+ 4 my-package::.result.)))
     7
     => 3

     However, this is already a modularity violation and is not something
     anyone should do unless they (a) had seen the sources and (b) didn't
     mind having it broken if I reimplemented it.]
But the point is there no risk of it happening by accident.

I don't know that I recommend this technique, btw.  I merely observe that
I've seen it many places, and I may have even copied the idea myself on
some occasions where I just wasn't up to fussing with gensyms.