From: Vladimir V. Zolotych
Subject: avoiding global vars.
Date: 
Message-ID: <3AD31F41.8B6E479@eurocom.od.ua>
Hello

- Is it possible to declare LINE to be special in the 
  following fragment:

  (defun foo ()
    (print line))
  (defun bar ()
    (loop for line ... from 0 to 10
          ;; (declare (special line))
	  do (foo)))

- Suppose I need to remember some value from one call of a
  function to another. E.g.

  (defun foo (a)
    (if (equal a old-a)
      ;; Use old value
      ;; Do some actions on new A and save A in OLD-A.

  In C I'd describe OLD-A as a static. What is the conventional way
  doing such things in CL ? Except "global" special variables of
  course. 

- Is it possible to have more than one filename extension in
  DEFSYSTEM (clocc.sourceforge.net, version 1.21) simultaneously ?
  I mean *filename-extensions* variable.

- MaiSQL issue. Sometimes convenient to know some info about just
  inserted record (most frequently the generated id). The following
  small correction would allow that.

  diff -r1.4 postgresql-sql.cl
  113c113,116
  < 	      t)
  ---
  > 	      (let ((oid-status (pqoidstatus result)))
  > 		(if (string/= oid-status "")
  > 		  (parse-integer oid-status)
  > 		  t)))

  (in database-execute-command method).

  Using PQoidValue would be more convenient, but it's supported only
  by recent Postgreses.


-- 
Vladimir Zolotych                         ······@eurocom.od.ua

From: Kent M Pitman
Subject: Re: avoiding global vars.
Date: 
Message-ID: <sfwpuekhjan.fsf@world.std.com>
"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:

> Hello
> 
> - Is it possible to declare LINE to be special in the 
>   following fragment:
> 
>   (defun foo ()
>     (print line))
>   (defun bar ()
>     (loop for line ... from 0 to 10
>           ;; (declare (special line))
> 	  do (foo)))

It might work to put the (declare (special line)) in the do.
Can't remember if that's conforming, but you could and should check CLHS
for such things.  (Did you?)

But really, you can avoid this whole problem by doing the stylistically
thing and declaring the communication variable globaly 
and then use it without local declaration, as in:

  (defvar *line*)
  (defun foo () (print *line*)) 
  (defun bar ()
    (loop for *line* ....))

Or, though it works worse for debugging, you can use the same trick as
used for old-a below.

> - Suppose I need to remember some value from one call of a
>   function to another. E.g.
> 
>   (defun foo (a)
>     (if (equal a old-a)
>       ;; Use old value
>       ;; Do some actions on new A and save A in OLD-A.
> 
>   In C I'd describe OLD-A as a static. What is the conventional way
>   doing such things in CL ? Except "global" special variables of
>   course. 

The most common way is, as you suggest, a global special.  It's easiest
to debug and I see no reason not to use it for most purposes.

You could also do the following, though it's a pain to debug if you need
to see interactively what's in old-a:

(let ((old-a :nothing-yet))

(defun foo (a)
  (if (equal a old-a)
      ...)
  ...)

);tel

I recommend not indenting the defun inside the let because many text editors
prefer to see defun in column 0, and  get confused when it's not.

[No opinion on your remaining questions since I don't use those facilities.]
From: Vladimir V. Zolotych
Subject: Re: avoiding global vars.
Date: 
Message-ID: <3AD346C2.10E87CAF@eurocom.od.ua>
Kent M Pitman wrote:
> 
> It might work to put the (declare (special line)) in the do.
> Can't remember if that's conforming, but you could and should check CLHS
> for such things.  (Did you?)

Yes, but didn't found confirmation.
This can mean two things: 1) no such possibility 
2) I didn't find/uderstand it. It is more likely
the first is true, but the second is possible also.
So I ask, your answer would be lesson for me how
to read CLHS. I can't say I know it well enough,
or read it always w/o difficulty.

-- 
Vladimir Zolotych                         ······@eurocom.od.ua
From: Kent M Pitman
Subject: Re: avoiding global vars.
Date: 
Message-ID: <sfwsnjgwmuw.fsf@world.std.com>
"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:

> Kent M Pitman wrote:
> > 
> > It might work to put the (declare (special line)) in the do.
> > Can't remember if that's conforming, but you could and should check CLHS
> > for such things.  (Did you?)
> 
> Yes, but didn't found confirmation.
> This can mean two things: 1) no such possibility 
> 2) I didn't find/uderstand it. It is more likely
> the first is true, but the second is possible also.
> So I ask, your answer would be lesson for me how
> to read CLHS. I can't say I know it well enough,
> or read it always w/o difficulty.

Well, I admit it's not perfectly well indexed so one could still overlook
something, but the bnf, for example, says all the things after do have
to be compound forms (which does not include declarations).  So that
dispenses with that hope, I guess.  Instead of:

   (loop for *line* = ...
         do ... stuff with *line* ...)

try maybe:

   (loop for line = ...
         do (let ((*line* line))
              ... stuff with *line* ...))

or better still:

  (loop (let ((*line* ...))
          (declare (special *line*))
          ...))
From: Will Deakin
Subject: Re: avoiding global vars.
Date: 
Message-ID: <3AD3262D.8030303@pindar.com>
Vladimir V. Zolotych wrote:

> Hello
Hello.

> - Is it possible to declare LINE to be special in the 
>   following fragment:
> 
>   (defun foo ()
>     (print line))
>   (defun bar ()
>     (loop for line ... from 0 to 10
>           ;; (declare (special line))
> 	  do (foo)))

I am not sure if this is what you want, there is labels that 
allows the declaration of local variable.

So
(defun bar ()
   (let (line)
     (labels ((defun foo ()
                 (print line))
        (loop for line ... from 0 to 10
              ;; (declare (special line))
               do (foo)))))

NB. I have not tried this.

> - Suppose I need to remember some value from one call of a
>   function to another. E.g.
Try a closure, such as:

(let ((a 342))
   (defun foo (a)
     (if (equal a old-a)
       ;; Use old value
       ;; Do some actions on new A and save A in OLD-A.
   ...blah...blah...

Sorry, I can't help with the rest of the stuff.

Best Regards,

:)will
From: Will Deakin
Subject: Re: avoiding global vars.
Date: 
Message-ID: <3AD3270A.2070600@pindar.com>
Will wrote:

> declaration of local variable.
                  ^^^^^ ^^^^^^^^
Arse. Make that local function....
From: Kent M Pitman
Subject: Re: avoiding global vars.
Date: 
Message-ID: <sfwitkcn0y5.fsf@world.std.com>
"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:
[...]

Well, nevermind what he wrote.  I want to go back to the subject line,
which is bugging me.

Can you say why are global variables to be avoided?  I think there
are soemtimes reasons, but often I think the reasons are imagined.

For example,

(let ((i 0))
 (defun counter () (incf i)))

doesn't have a global variable, but I'd like to hear arguments that
the following is, a priori, to be avoided as generally equivalent and
possibly superior.

(defvar *counter* 0)
(defun counter () (incf *counter*))
From: ········@hex.net
Subject: Re: avoiding global vars.
Date: 
Message-ID: <82IA6.140638$lj4.4343796@news6.giganews.com>
Kent M Pitman <······@world.std.com> writes:
> "Vladimir V. Zolotych" <······@eurocom.od.ua> writes:
> [...]
> Well, nevermind what he wrote.  I want to go back to the subject line,
> which is bugging me.
> 
> Can you say why are global variables to be avoided?  I think there
> are soemtimes reasons, but often I think the reasons are imagined.
> 
> For example,
> 
> (let ((i 0))
>  (defun counter () (incf i)))
> 
> doesn't have a global variable, but I'd like to hear arguments that
> the following is, a priori, to be avoided as generally equivalent and
> possibly superior.
> 
> (defvar *counter* 0)
> (defun counter () (incf *counter*))

The thing I'd question about the "LET" version is just how you're
supposed to get at the counter value.

CLISP and CMU/CL both do what I'd expect:

[8]> (let ((i 0)) (defun counter () (incf i)))
COUNTER
[9]> (counter)
1
[10]> (counter)
2
[11]> i
*** - EVAL: variable I has no value
1. Break [12]> 

As for trying to avoid globals, I'd think the "evil" to be that having
vast quantities of them not sitting in structures is rather like using
lots of GOTOs.  Obviously code's going to contain them, but you
certainly want to keep it structured...
-- 
(reverse (concatenate 'string ····················@" "454aa"))
http://vip.hex.net/~cbbrowne/resume.html
"... While programs written for Sun machines won't run unmodified on
Intel-based computers, Sun said the two packages will be completely
compatible and that software companies can convert a program from one
system to the other through a fairly straightforward and automated
process known as ``recompiling.''" -- San Jose Mercury News
From: Duane Rettig
Subject: Re: avoiding global vars.
Date: 
Message-ID: <4itkca9ot.fsf@beta.franz.com>
········@hex.net writes:

> The thing I'd question about the "LET" version is just how you're
> supposed to get at the counter value.
> 
> CLISP and CMU/CL both do what I'd expect:
> 
> [8]> (let ((i 0)) (defun counter () (incf i)))
> COUNTER
> [9]> (counter)
> 1
> [10]> (counter)
> 2
> [11]> i
> *** - EVAL: variable I has no value
> 1. Break [12]> 

Try:

(let ((i 0))

(defun counter () (incf i))

(defun counter-value () i)

(defun set-counter-value (new-value) (setq i new-value))
)

You could get fancier and define the third function as a setf-method.
Or you could get _really_ fancy and use symbol-macros on i.  But of
course, you would want to establish a naming convention for such
globally accessible closed-over variables...

> As for trying to avoid globals, I'd think the "evil" to be that having
> vast quantities of them not sitting in structures is rather like using
> lots of GOTOs.  Obviously code's going to contain them, but you
> certainly want to keep it structured...

It's a namespace and accessibility issue, and thus a religious one.
An argument for the global-variable approach is that it provides
for global access.  An argument for the closed-over variable approach
is that it provides for limited access.

-- 
Duane Rettig          Franz Inc.            http://www.franz.com/ (www)
1995 University Ave Suite 275  Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253   ·····@Franz.COM (internet)
From: ········@hex.net
Subject: Re: avoiding global vars.
Date: 
Message-ID: <fWJA6.140790$lj4.4354431@news6.giganews.com>
Duane Rettig <·····@franz.com> writes:
> ········@hex.net writes:
> > The thing I'd question about the "LET" version is just how you're
> > supposed to get at the counter value.
> > 
> > CLISP and CMU/CL both do what I'd expect:
> > 
> > [8]> (let ((i 0)) (defun counter () (incf i)))
> > COUNTER
> > [9]> (counter)
> > 1
> > [10]> (counter)
> > 2
> > [11]> i
> > *** - EVAL: variable I has no value
> > 1. Break [12]> 
> 
> Try:
> 
> (let ((i 0))
> 
> (defun counter () (incf i))
> 
> (defun counter-value () i)
> 
> (defun set-counter-value (new-value) (setq i new-value))
> )

> You could get fancier and define the third function as a
> setf-method.  Or you could get _really_ fancy and use symbol-macros
> on i.  But of course, you would want to establish a naming
> convention for such globally accessible closed-over variables...

No disagreement, but I think I'd be inclined, by the time I have this
many methods, to head to something a little more sophisticated still,
and create a CLOS "counter" class.

>> As for trying to avoid globals, I'd think the "evil" to be that
>> having vast quantities of them not sitting in structures is rather
>> like using lots of GOTOs.  Obviously code's going to contain them,
>> but you certainly want to keep it structured...

> It's a namespace and accessibility issue, and thus a religious one.
> An argument for the global-variable approach is that it provides for
> global access.  An argument for the closed-over variable approach is
> that it provides for limited access.

.. which your setf-method winds up demolishing :-(.
-- 
(concatenate 'string "aa454" ·@freenet.carleton.ca")
http://vip.hex.net/~cbbrowne/resume.html
I hate wet paper bags.
From: Duane Rettig
Subject: Re: avoiding global vars.
Date: 
Message-ID: <4snjgwhis.fsf@beta.franz.com>
········@hex.net writes:

> Duane Rettig <·····@franz.com> writes:
> > ········@hex.net writes:
> > > The thing I'd question about the "LET" version is just how you're
> > > supposed to get at the counter value.
> > > 
> > > CLISP and CMU/CL both do what I'd expect:
> > > 
> > > [8]> (let ((i 0)) (defun counter () (incf i)))
> > > COUNTER
> > > [9]> (counter)
> > > 1
> > > [10]> (counter)
> > > 2
> > > [11]> i
> > > *** - EVAL: variable I has no value
> > > 1. Break [12]> 
> > 
> > Try:
> > 
> > (let ((i 0))
> > 
> > (defun counter () (incf i))
> > 
> > (defun counter-value () i)
> > 
> > (defun set-counter-value (new-value) (setq i new-value))
> > )
> 
> > You could get fancier and define the third function as a
> > setf-method.  Or you could get _really_ fancy and use symbol-macros
> > on i.  But of course, you would want to establish a naming
> > convention for such globally accessible closed-over variables...
> 
> No disagreement, but I think I'd be inclined, by the time I have this
> many methods, to head to something a little more sophisticated still,
> and create a CLOS "counter" class.

Yes.  My intention was that the code should be taken seriously and
the paragraph later be taken slightly tongue-in-cheek.  Perhaps I
was too subtle and should have put a :-) in there explicitly.

I have, however, used the closure style at times to obfuscate
compiled code without making it harder to read the source.

> >> As for trying to avoid globals, I'd think the "evil" to be that
> >> having vast quantities of them not sitting in structures is rather
> >> like using lots of GOTOs.  Obviously code's going to contain them,
> >> but you certainly want to keep it structured...
> 
> > It's a namespace and accessibility issue, and thus a religious one.
> > An argument for the global-variable approach is that it provides for
> > global access.  An argument for the closed-over variable approach is
> > that it provides for limited access.
> 
> .. which your setf-method winds up demolishing :-(.

Absolutely :-)

-- 
Duane Rettig          Franz Inc.            http://www.franz.com/ (www)
1995 University Ave Suite 275  Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253   ·····@Franz.COM (internet)
From: Kent M Pitman
Subject: Re: avoiding global vars.
Date: 
Message-ID: <sfwvgocwndn.fsf@world.std.com>
········@hex.net writes:

> As for trying to avoid globals, I'd think the "evil" to be that having
> vast quantities of them not sitting in structures is rather like using
> lots of GOTOs.  Obviously code's going to contain them, but you
> certainly want to keep it structured...

I think the question is how you do the bookkeeping.  For example, 
who ever names a variable without checking whether it's already in use?
So the structure is "there's a registry" (the package) and there's a 
facility for checking to see if a name is in use (find-symbol).  So what
is unstructured about that?  Further, what is "more" structured about
having a lexical?  It certainly doesn't have "structure" enough to let you
read the variable value if you didn't anticipate the need to do so in advance
without going into the debugger and peeking into structures you were never 
meant to peek at.

And let's not go to the GOTO's example, since there is a formula for turning
gotos into LABELs and everyone likes labels.  I don't think there is more
structure in a labels than in a goto.

I think this is all an imagined problem with terrible terminology surrounding
it.
From: Rob Warnock
Subject: Re: avoiding global vars.
Date: 
Message-ID: <9b6jn5$2hqo$1@fido.engr.sgi.com>
<········@hex.net> wrote:
+---------------
| Kent M Pitman <······@world.std.com> writes:
| > Can you say why are global variables to be avoided?  I think there
| > are soemtimes reasons, but often I think the reasons are imagined.
...
| As for trying to avoid globals, I'd think the "evil" to be that having
| vast quantities of them not sitting in structures is rather like using
| lots of GOTOs.  Obviously code's going to contain them, but you
| certainly want to keep it structured...
+---------------

Indeed:

	Wulf, W. A., and Shaw, M., "Global Variable Considered Harmful",
	SIGPLAN Notices Vol. 8, No. 2, February 1973. 

which is one of many replies to:

	Edsger W. Dijkstra, "Go To Statement Considered Harmful",
	Letters to the Editor, CACM, Vol. 11, No. 3, March 1968.

Wulf & Shaw showed that one can trivially map arbitrary "GOTO"-filled
spagetti code to GOTO-less "pure" structured programming style using
a single LOOP containing a single CASE, and a single global variable.
Simply label each basic block of the spagetti code with a number, then:

	(defvar *pc* 0)

	(loop
	  (case *pc*
	    (0 ...block 0 goes here... (setf *pc* (if (some_test) 3 17)))
	    (1 ...block 1 goes here... (setf *pc* (if (other_test) 22 96)))
	    (2 ...block 2 goes here... (setf *pc* (if (y_a_test) 53 35)))
	    ...and so on...))

However, if you're of the functional programming persuasion, you might
say that what they *should* have called the paper was "Assignment To
Global Variable Considered Harmful", assignment being the real villain,
spaghetti-wise.


-Rob

-----
Rob Warnock, 31-2-510		····@sgi.com
SGI Network Engineering		<URL:http://reality.sgi.com/rpw3/>
1600 Amphitheatre Pkwy.		Phone: 650-933-1673
Mountain View, CA  94043	PP-ASEL-IA
From: Kent M Pitman
Subject: Re: avoiding global vars.
Date: 
Message-ID: <sfw7l0o28xc.fsf@world.std.com>
····@rigden.engr.sgi.com (Rob Warnock) writes:

> 
> <········@hex.net> wrote:
> +---------------
> | Kent M Pitman <······@world.std.com> writes:
> | > Can you say why are global variables to be avoided?  I think there
> | > are soemtimes reasons, but often I think the reasons are imagined.
> ...
> | As for trying to avoid globals, I'd think the "evil" to be that having
> | vast quantities of them not sitting in structures is rather like using
> | lots of GOTOs.  Obviously code's going to contain them, but you
> | certainly want to keep it structured...
> +---------------
> 
> Indeed:
> 
> 	Wulf, W. A., and Shaw, M., "Global Variable Considered Harmful",
> 	SIGPLAN Notices Vol. 8, No. 2, February 1973. 
> 
> which is one of many replies to:
> 
> 	Edsger W. Dijkstra, "Go To Statement Considered Harmful",
> 	Letters to the Editor, CACM, Vol. 11, No. 3, March 1968.
> 
> Wulf & Shaw showed that one can trivially map arbitrary "GOTO"-filled
> spagetti code to GOTO-less "pure" structured programming style using
> a single LOOP containing a single CASE, and a single global variable.
> Simply label each basic block of the spagetti code with a number, then:
> 
> 	(defvar *pc* 0)
> 
> 	(loop
> 	  (case *pc*
> 	    (0 ...block 0 goes here... (setf *pc* (if (some_test) 3 17)))
> 	    (1 ...block 1 goes here... (setf *pc* (if (other_test) 22 96)))
> 	    (2 ...block 2 goes here... (setf *pc* (if (y_a_test) 53 35)))
> 	    ...and so on...))
> 
> However, if you're of the functional programming persuasion, you might
> say that what they *should* have called the paper was "Assignment To
> Global Variable Considered Harmful", assignment being the real villain,
> spaghetti-wise.

Actually, Scheme has a very nice syntactic rewrite from goto's to labels
(hence the name labels, btw) which causes me to believe that if there's
an isomorphism of structure between the "unstructured" and the "structured"
then there's something fishy going on with the religious nomenclature.

  (prog ()
        (f0a) (f0b) (f0c) ...
    t1  (f1a) (f1b) (f1c) ...
    t2  (f2a) (f2b) (f2c) ...
    ...
    tN  (fNa) (fNb) (fNc) ...)

  where any (fxa), etc. potentially lexically involves (go t1), (go t2), 
  etc. or (return v), and where the whole expression returns false if it
  falls off the end.

  ==becomes==>

  (catch return
    ((catch go
       (labels ((t0 () (f0a) (f0b) (f0c) ... (go t1))
                (t1 () (f1a) (f1b) (f1c) ... (go t2))
                (t2 () (f2a) (f2b) (f2c) ... (go t3))
                ...
                (tN () (fNa) (fNb) (fNc) ... (return #f)))
         t0))))

  or in more modern parlance (post-"Revised Report", where they/we renamed
  scheme to call-with-current-continuation and things got more ugly):

  (call-with-current-continuation
    (lambda (return)
      ((call-with-current-continuation
	 (lambda (go)
	   (labels ((t0 () (f0a) (f0b) (f0c) ... (go t1))
		    (t1 () (f1a) (f1b) (f1c) ... (go t2))
		    (t2 () (f2a) (f2b) (f2c) ... (go t3))
		    ...
		    (tN () (fNa) (fNb) (fNc) ... (return #f)))
	     t0))))))

I did this from memory and hope I've not screwed it up.  The right form of
this, which this might or might not be, was discovered and taught to me by
Jonathan Rees, based on a variant form Steele had put in one of his papers.
(I'll run this by Jonathan just to be sure I didn't screw it up and post
any corrections.)  But the point is, it seems wrong to me to talk about
the untransformed thing as unstructured and the transformed thing as 
structured when it hasn't really changed shape in the transformation.

- - - - 

And back to the original issue of globality, I observe that CL doesn't
really have a "global" because everything is contained in a package.
A package in CL fucntions in some ways like a "class" in Java, to the
extent that classes in Java are overloaded to be not only a way of
controlling behavior but also a packaged form of delivering code
modules.  CL packages and Java classes both pull everything in a set
of functionality into a neat little bundle with only a part of that
bundle being exported, and establishing a bounding region over which
anyone has to walk in order to make statements about what the code
will do, and allowing introspective capabilities that can ask
questions about what is in the package.  Also, since not all code is
written in the same package (or, at least, it shouldn't be; no one
should be writing serious code in CL-USER, for example), there is no
"global" any more than a variable declaration at top level of a Java
class is global.
From: Kent M Pitman
Subject: Re: avoiding global vars.
Date: 
Message-ID: <sfwu23srfyq.fsf@world.std.com>
Kent M Pitman <······@world.std.com> writes:

> Actually, Scheme has a very nice syntactic rewrite from goto's to labels
> (hence the name labels, btw) which causes me to believe that if there's
> an isomorphism of structure between the "unstructured" and the "structured"
> then there's something fishy going on with the religious nomenclature.
> 
>   (prog ()
>         (f0a) (f0b) (f0c) ...
>     t1  (f1a) (f1b) (f1c) ...
>     t2  (f2a) (f2b) (f2c) ...
>     ...
>     tN  (fNa) (fNb) (fNc) ...)
> 
>   where any (fxa), etc. potentially lexically involves (go t1), (go t2), 
>   etc. or (return v), and where the whole expression returns false if it
>   falls off the end.
> 
>   ==becomes==>
> 
>   (catch return
>     ((catch go
>        (labels ((t0 () (f0a) (f0b) (f0c) ... (go t1))
>                 (t1 () (f1a) (f1b) (f1c) ... (go t2))
>                 (t2 () (f2a) (f2b) (f2c) ... (go t3))
>                 ...
>                 (tN () (fNa) (fNb) (fNc) ... (return #f)))
>          t0))))
> 
>   or in more modern parlance (post-"Revised Report", where they/we renamed
>   scheme to call-with-current-continuation and things got more ugly):
> 
>   (call-with-current-continuation
>     (lambda (return)
>       ((call-with-current-continuation
> 	 (lambda (go)
> 	   (labels ((t0 () (f0a) (f0b) (f0c) ... (go t1))
> 		    (t1 () (f1a) (f1b) (f1c) ... (go t2))
> 		    (t2 () (f2a) (f2b) (f2c) ... (go t3))
> 		    ...
> 		    (tN () (fNa) (fNb) (fNc) ... (return #f)))
> 	     t0))))))
> 
> I did this from memory and hope I've not screwed it up.  The right form of
> this, which this might or might not be, was discovered and taught to me by
> Jonathan Rees, based on a variant form Steele had put in one of his papers.
> (I'll run this by Jonathan just to be sure I didn't screw it up and post
> any corrections.) [...]

Here's what I got back from Jonathan Rees:

| Basically right.  2 things: typo
| 
|      "they/we renamed scheme to"
|  ==> "they/we renamed catch to"
| 

[oops. right.  heh.]

| and, in Revised^3 Scheme and beyond, "labels" is renamed "letrec"
| (that was a big fight) and no longer takes defun-type syntax, ergo the
| even worse
| 
| 	   (letrec ((t0 (lambda () (f0a) (f0b) (f0c) ... (go t1)))
| 		    (t1 (lambda () (f1a) (f1b) (f1c) ... (go t2)))
| 		    (t2 (lambda () (f2a) (f2b) (f2c) ... (go t3)))
| 		    ...
| 		    (tN (lambda () (fNa) (fNb) (fNc) ... (return #f))))
| 	     t0)
| 
| Of course, those final (go ti)'s could be simple tail-call (ti)'s, but
| probably things are clearer using go.

[Yes, I used GO just for clarity.]

Jonathan also added this observation:

| My two bits is that we have to talk about structured and unstructured
| *programs*, not structured and unstructured language constructs.  You
| can write structured code (i.e. code whose control structure is easy
| to understand) with gotos, and you can write unstructured code with
| Pascal WHILE loops.
From: Hannah Schroeter
Subject: Re: avoiding global vars.
Date: 
Message-ID: <9bkas0$sck$1@c3po.schlund.de>
Hello!

In article <·············@fido.engr.sgi.com>,
Rob Warnock <····@rigden.engr.sgi.com> wrote:
>[...]

>	(defvar *pc* 0)

>	(loop
>	  (case *pc*
>	    (0 ...block 0 goes here... (setf *pc* (if (some_test) 3 17)))
>	    (1 ...block 1 goes here... (setf *pc* (if (other_test) 22 96)))
>	    (2 ...block 2 goes here... (setf *pc* (if (y_a_test) 53 35)))
>	    ...and so on...))

>However, if you're of the functional programming persuasion, you might
>say that what they *should* have called the paper was "Assignment To
>Global Variable Considered Harmful", assignment being the real villain,
>spaghetti-wise.

Remove the word "global", as you can avoid both goto and global
variable assignment thus:

(let ((pc 0))
  (loop
    (case pc
      (0 ... (setf pc ...))
      (42 ... (setf pc ...))
      ...)))

Kind regards,

Hannah.
From: Will Deakin
Subject: Re: avoiding global vars.
Date: 
Message-ID: <3AD41384.2000107@pindar.com>
Kent M Pitman wrote:

> doesn't have a global variable, but I'd like to hear arguments that
> the following is, a priori, to be avoided as generally equivalent and
> possibly superior.

Uhhh. To explain why I answered the way I did maybe due to a bad 
experience I had support programming in the unexcellent ANSI 
language PL/B.

The flavour of the language we were using had the serious 
problems that (IIRC) there was the way to pass variables between 
functions was to use global variables, only the first six + the 
last character were recognised in variable names (when 
compiled[1] here_we_are and here_we_are_again_here are the *same* 
variable) and there was only a single namespace for variables and 
functions. Thinking about it still makes me wince...

Clearly that has (in the case of cl, in an inappropriate fashion) 
coloured my world view wrt global variables.  On further 
reflection, I have realise that I prefer closures because they 
are more cool than global variables. Which is a really bad reason 
to do anything.

Cheers,

:) will

[1] Ooops. The language wasn't compiled -- it wasn't compiled -- 
it was interpreted!
From: Lieven Marchand
Subject: Re: avoiding global vars.
Date: 
Message-ID: <m3wv8r8jy4.fsf@localhost.localdomain>
Will Deakin <········@pindar.com> writes:

> Uhhh. To explain why I answered the way I did maybe due to a bad 
> experience I had support programming in the unexcellent ANSI 
> language PL/B.
> 
> The flavour of the language we were using had the serious 
> problems that (IIRC) there was the way to pass variables between 
> functions was to use global variables, only the first six + the 
> last character were recognised in variable names (when 
> compiled[1] here_we_are and here_we_are_again_here are the *same* 
> variable) and there was only a single namespace for variables and 
> functions. Thinking about it still makes me wince...

Many people seem perfectly happy in the ANSI C language which has
practically a single namespace and where the standard guarantees only
six characters for identifiers with external linkage. Porting stuff
from the Unix world to an IBM mainframe generally involved creating
large include files containing only lines of the form #define
typically_long_unix_identifier ux1234.

-- 
Lieven Marchand <···@wyrd.be>
Gla�r ok reifr skyli gumna hverr, unz sinn b��r bana.
From: Thomas F. Burdick
Subject: Re: avoiding global vars.
Date: 
Message-ID: <xcvg0ffrv2r.fsf@tsunami.OCF.Berkeley.EDU>
Lieven Marchand <···@wyrd.be> writes:

> Many people seem perfectly happy in the ANSI C language which has
> practically a single namespace and where the standard guarantees only
> six characters for identifiers with external linkage. Porting stuff
> from the Unix world to an IBM mainframe generally involved creating
> large include files containing only lines of the form #define
> typically_long_unix_identifier ux1234.

Hmm, I think very few people are happy to accommodate ANSI C's six
character limit on identifiers; they are, in fact, happy to sacrifice
portability in order to use the language extension that allows them
nice_long_descriptive_identifiers_in_unix.  If they were happy with
this limitation in ANSI C, porters to IBM wouldn't need to do things
like ux1234.
From: Lieven Marchand
Subject: Re: avoiding global vars.
Date: 
Message-ID: <m366ga6nbe.fsf@localhost.localdomain>
···@tsunami.OCF.Berkeley.EDU (Thomas F. Burdick) writes:

> Lieven Marchand <···@wyrd.be> writes:
> 
> > Many people seem perfectly happy in the ANSI C language which has
> > practically a single namespace and where the standard guarantees only
> > six characters for identifiers with external linkage. Porting stuff
> > from the Unix world to an IBM mainframe generally involved creating
> > large include files containing only lines of the form #define
> > typically_long_unix_identifier ux1234.
> 
> Hmm, I think very few people are happy to accommodate ANSI C's six
> character limit on identifiers; they are, in fact, happy to sacrifice
> portability in order to use the language extension that allows them
> nice_long_descriptive_identifiers_in_unix.  If they were happy with
> this limitation in ANSI C, porters to IBM wouldn't need to do things
> like ux1234.

Most open source Unix software in these days suffered from what Henry
Spencer calls the "All the world is a Vax" syndrome in his ten
commandments of the C programmer. Just like today most open source
Unix software thinks every system is Linux. It is possible to write
portable in C/Posix but most Unix programmers don't bother. In the C
community, possession and knowledge of the language standard is far
less widespread than e.g. in the CL or the Ada community. In that
sense people are not writing ANSI C code, they are writing Visual
whatever code or gcc code.

-- 
Lieven Marchand <···@wyrd.be>
Gla�r ok reifr skyli gumna hverr, unz sinn b��r bana.
From: Kent M Pitman
Subject: Re: avoiding global vars.
Date: 
Message-ID: <sfweluzdvig.fsf@world.std.com>
Lieven Marchand <···@wyrd.be> writes:

> Will Deakin <········@pindar.com> writes:
> 
> > Uhhh. To explain why I answered the way I did maybe due to a bad 
> > experience I had support programming in the unexcellent ANSI 
> > language PL/B.
> > 
> > The flavour of the language we were using had the serious 
> > problems that (IIRC) there was the way to pass variables between 
> > functions was to use global variables, only the first six + the 
> > last character were recognised in variable names (when 
> > compiled[1] here_we_are and here_we_are_again_here are the *same* 
> > variable) and there was only a single namespace for variables and 
> > functions. Thinking about it still makes me wince...
> 
> Many people seem perfectly happy in the ANSI C language which has
> practically a single namespace and where the standard guarantees only
> six characters for identifiers with external linkage. Porting stuff
> from the Unix world to an IBM mainframe generally involved creating
> large include files containing only lines of the form #define
> typically_long_unix_identifier ux1234.

Well, I guess my point here was to trick someone into saying words like
"name clashes" and stuff like that.  But Lisp's package system makes it
so that although there is the occasional risk of package name conflict,
basically there is no major issue about name conflicts.  So the notion
of "global" is somewhat of a misnomer since it's not really global in 
the sense of being shared by unrelated modules.
From: Frode Vatvedt Fjeld
Subject: Re: avoiding global vars.
Date: 
Message-ID: <2hpuekbvs2.fsf@dslab7.cs.uit.no>
"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:

> - Is it possible to declare LINE to be special in the 
>   following fragment:
> 
>   (defun foo ()
>     (print line))
>   (defun bar ()
>     (loop for line ... from 0 to 10
>           ;; (declare (special line))
> 	  do (foo)))

I don't think so, but how about

  (defun bar ()
    (let (*line*)
      (declare (special *line*))
      (loop for line from 0 to 10
         do (setf *line* line)
         do (foo))))

  (defun foo ()
    (declare (special *line*))
    (print *line*))

Any particular reason you can't pass LINE as an argument to foo?

> In C I'd describe OLD-A as a static. What is the conventional way
> doing such things in CL ? Except "global" special variables of
> course.

The conventional way in lisp would be to use a (global) DEFVAR. Note
that a C "static" variable is also just a global variable, as are
(lisp) variables enclosed by DEFUN, because DEFUN per definition
creates global closures (functions).

  (defvar *old-a*)
  (defun foo (a)
    ...)

or if for some strange reason you want OLD-A to be hidden "inside" the
function FOO, use a closure. But I'd discourage it.

  (let (old-a)
    (defun foo (a)
       ...))

-- 
Frode Vatvedt Fjeld
From: Will Deakin
Subject: Re: avoiding global vars.
Date: 
Message-ID: <3AD32DED.1080009@pindar.com>
Frode Vatvedt Fjeld wrote:

> or if for some strange reason you want OLD-A to be hidden "inside" the
> function FOO, use a closure. But I'd discourage it.
> 
>   (let (old-a)
>     (defun foo (a)
>        ...))

Out of interest, why would you discourage it?

:)will
From: Frode Vatvedt Fjeld
Subject: Re: avoiding global vars.
Date: 
Message-ID: <2hlmp8buvw.fsf@dslab7.cs.uit.no>
Will Deakin <········@pindar.com> writes:

> Out of interest, why would you discourage it?

Because there are no clear benefits as I see it to having global
closures. You'll inevitably have problems when you need to reset or
monitor the variable, and also it doesn't look so good textually :)

Protecting access to variables perhaps makes some sense in static
languages like C, but in dynamic languages like lisp it really
doesn't.

Also for readablility, keeping global state in global *variables* is
good style, IMHO.

Actually there might be a _slight_ performance benefit in using
closures over global specials. But I'm hesitant event to mention it,
because it's really no argument in favor of global closures except
perhaps in some extreme situations.

-- 
Frode Vatvedt Fjeld
From: Geoff Summerhayes
Subject: Re: avoiding global vars.
Date: 
Message-ID: <td6f918aoq4kee@corp.supernews.com>
"Frode Vatvedt Fjeld" <······@acm.org> wrote in message ···················@dslab7.cs.uit.no...
> Will Deakin <········@pindar.com> writes:
>
> > Out of interest, why would you discourage it?
>
> Because there are no clear benefits as I see it to having global
> closures. You'll inevitably have problems when you need to reset or
> monitor the variable, and also it doesn't look so good textually :)
>
> Protecting access to variables perhaps makes some sense in static
> languages like C, but in dynamic languages like lisp it really
> doesn't.
>
> Also for readablility, keeping global state in global *variables* is
> good style, IMHO.
>
> Actually there might be a _slight_ performance benefit in using
> closures over global specials. But I'm hesitant event to mention it,
> because it's really no argument in favor of global closures except
> perhaps in some extreme situations.
>

A global getting trashed in any language is a difficult to find bug
because you see the failure at the point of use, not the point of
the actual cause. I don't see how lisp is going to save you from this
kind of error although I suspect we are getting into the "Doctor,
doctor, it hurts when I do this"-"Don't do that!" area.

Geoff
From: Kent M Pitman
Subject: Re: avoiding global vars.
Date: 
Message-ID: <sfwu23wwn6w.fsf@world.std.com>
"Geoff Summerhayes" <·············@hNoOtSmPaAiMl.com> writes:

> A global getting trashed in any language is a difficult to find bug
> because you see the failure at the point of use, not the point of
> the actual cause. I don't see how lisp is going to save you from this
> kind of error although I suspect we are getting into the "Doctor,
> doctor, it hurts when I do this"-"Don't do that!" area.

Granted data getting trashed in any language is difficult to find, but how
is it made easier to find by making it be a global lexical or even a local
lexical?  Either the problem can happen or not, and at the point it can happen
surely a special variable is the easiest to debug since it's easy to poke at
such a value from other functions or interactively.  That reduces the question
to whether it's likely to happen by accident.  I have never in my life written
code like

(defvar *gensymbol-counter* 0)
(defun gensymbol () 
  (make-symbol (format nil "G~D" (incf *gensymbol-counter*))))

and then later "accidentally" set the variable *gensymbol-counter* nor found
it "trashed".  Surely, there are cases where you make a "communication 
variable" that a number of functions share and where there are myriad uses
and one bashes things.  But many of those would not get easier to debug if
a lexical were involved...
From: Geoffrey Summerhayes
Subject: Re: avoiding global vars.
Date: 
Message-ID: <QLPA6.550032$f36.16597924@news20.bellglobal.com>
"Kent M Pitman" <······@world.std.com> wrote in message
····················@world.std.com...
>
> Granted data getting trashed in any language is difficult to find, but how
> is it made easier to find by making it be a global lexical or even a local
> lexical?  Either the problem can happen or not, and at the point it can
happen
> surely a special variable is the easiest to debug since it's easy to poke
at
> such a value from other functions or interactively.  That reduces the
question
> to whether it's likely to happen by accident.  I have never in my life
written
> code like
>
> (defvar *gensymbol-counter* 0)
> (defun gensymbol ()
>   (make-symbol (format nil "G~D" (incf *gensymbol-counter*))))
>
> and then later "accidentally" set the variable *gensymbol-counter* nor
found
> it "trashed".  Surely, there are cases where you make a "communication
> variable" that a number of functions share and where there are myriad uses
> and one bashes things.  But many of those would not get easier to debug if
> a lexical were involved...

I suppose it's just my paranoia, but putting a variable in the global
namespace whose sole purpose is to keep state for ~[no??~;a~:;~:*~R~]
function~:P seems to me be overkill. Same with wrapping it in a class.
I suppose I could declare it inside a package and then unintern it.
All in all, I find sticking the function~:P in a let the easiest of the
options, it also forces a textual coupling, the function~:P can't
end up too far from the variable. If it turns out to be necessary
to alter it's visibility later at least it will be easy to find. Of
course, this depends on the number and complexity of functions relying
on the value.

sorry, been playing with format lately,
Geoff
From: Hallvard B Furuseth
Subject: Re: avoiding global vars.
Date: 
Message-ID: <HBF.20010410dp7u@bombur.uio.no>
Frode Vatvedt Fjeld <······@acm.org> writes:
>   (defun bar ()
>     (let (*line*)
>       (declare (special *line*))

or (locally (declare (special *line*)) ...)

>       (loop for line from 0 to 10
>          do (setf *line* line)
>          do (foo))))

-- 
Hallvard
From: Kent M Pitman
Subject: Re: avoiding global vars.
Date: 
Message-ID: <sfwk84sn15z.fsf@world.std.com>
Hallvard B Furuseth <············@usit.uio.no> writes:

> Frode Vatvedt Fjeld <······@acm.org> writes:
> >   (defun bar ()
> >     (let (*line*)
> >       (declare (special *line*))
> 
> or (locally (declare (special *line*)) ...)
> 
> >       (loop for line from 0 to 10
> >          do (setf *line* line)
> >          do (foo))))

Uh, be careful here.  You don't make it clear where you want the locally
but in general, SPECIAL declarations (as contrasted with proclamations)
do not pervade into contained bindings.

If in the above you mean the LOCALLY only around the LOOP (in place of
having done a declare), as in:

 (let (*line*)
   (locally (declare (special *line*))
     (loop ...)))

then you will have lexically bound *line* in the LET and then will do
special references "outward past" that lexical binding.

On the other hand, if you put it outside the LET, then it will affect
nothing. That is,

 (locally (declare (special *line*))
   (let (*line*)
     (loop ...)))

is the same as

 (let (*line*)
   (loop ...))

since the LET binding is unaffected.
From: Hallvard B Furuseth
Subject: Re: avoiding global vars.
Date: 
Message-ID: <HBF.20010411w04y@bombur.uio.no>
Kent M Pitman <······@world.std.com> writes:
>Hallvard B Furuseth <············@usit.uio.no> writes:
> 
> Uh, be careful here.  (...)

oops, thanks.

-- 
Hallvard