Subject: Re: free variables
From: (Rob Warnock)
Date: 1998/02/11
Newsgroups: comp.lang.scheme
Message-ID: <6brgdu$>

Zhiqing Liu  <> wrote:
| Is there a portable way to find if a variable is free in a Scheme
| environment? I don't think the following is portable:
| > a
| reference to undefined identifier: a

I was waiting to see if someone more authoritative would answer, but...

Basically, the answer is "No (but...)".  Actually, I suspect you're not
asking the question you really want to be asking -- about "undefined" or
"unbound" variables, not a "free" ones.

As I understand it, the term "free" is usually only applied to the body of a
lambda expression. Yes, in the example you gave "a" itself is an "expression",
but that expression contains no binding forms. Thus in your example the
variable "a" will *always* be free in the expression "a", regardless of
whether it's currently defined or not.

Practically speaking, except as a question one might ask of an editor when
modifying source text, it's not going to be very useful to know at run-time
whether a given variable is "free" or not -- there's nothing you can *do*
about it in any case. Yes, you might call "eval" to do a top-level "define",
but that's fairly ugly, and anyway "eval" semantics aren't every portable
either.  E.g., some take an environment as a 2nd arg and some don't. [And
besides, in a compiled environment all of the non-free variables may have
been renamed or turned into display offsets or something anyway, so you might
not even be able to *ask* the question...]

So I'm going to answer a different question, and hope it's what you were
really asking in the first place, namely:

  During the progress of a Scheme "load" (or while typing S-exprs at a
  top-level REPL) is there a portable way to find if a variable is currently
  already defined in the top-level environment?  Specifically, so that 
  a (re)definition of that variable can be "conditionalized" on its current
  state of defined-ness, much like one does in languages like "C" with

The answer to that one is: "No, not really, but several Scheme implementations
give you something that's somewhat usable when trying to make code portable
among them." Here are some specific examples:

- MzScheme has a "defined?" procedure (ordinary primitive procedure), which
  takes a symbol as an argument, and returns #t if the corresponding variable
  is defined in the top-level environment. MzScheme does not consider lambda-
  bound variables to be "defined" (probably because it internally "compiles"
  expressions before evaluating them, and so the names of lambda-bound
  variables have vanished before "defined?" could run).

- SIOD has a "symbol-bound?" procedure which works like MzScheme's "defined?".

- ELK has a "bound?" procedure, which takes a symbol as an argument, and
  returns #t if the corresponding variable is defined in the top-level
  environment *or* if it is lexically-bound/lambda-bound, unlike the others.

- SCM has a "defined?" macro form (*not* a procedure), which takes a literal
  variable name (*not* a symbol!!) as an argument, and returns #t if that
  variable is defined in the top-level environment. Like MzScheme, SCM does
  not consider lambda-bound variables to be "defined".

Now in SCM, (defined? '+) ==> #f and in the others (defined? '+) ==> #t,
so you can use that to tell them apart, so you can hack the difference as:

	(if (defined? '+)
	  (define repl:defined? defined?)
	  (define (repl:defined? x) (eval (list 'defined? x))))

[yes, it's an ugly use of "eval", but I couldn't think of any better way
to do it]  but then you still need to arrange [in an init file?] to get SIOD
to see (define defined? symbol-bound?) and Elk to see (define defined? bound?)
before running the above test. At that point, you can portably [well, among
those four!] do things like:

	(if (not (repl:defined? 'some-lib-function))
	  (load "some-lib-function.scm"))

Is that what you were asking?


Rob Warnock, 7L-551
Silicon Graphics, Inc.		Phone: 650-933-1673 [New area code!]
2011 N. Shoreline Blvd.		FAX: 650-933-4392
Mountain View, CA  94043	PP-ASEL-IA