This is the mail archive of the mailing list for the Guile project.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: Reintroducing old `defined?'

First of all, I think we should start with a clear definition of what
defined? does.  And we should try to describe what we want without
specifying whether it's syntax or a function, and then figure out
which option makes more sense.  Note that that question is independent
of whether defined? should evaluate its argument (i.e. whether we need
a quote or not).  For the time being, I'll assume it doesn't evaluate
its argument.

Here's what I think it should mean:

  (defined? ID) evaluates to #t if simply replacing the form with the
  identifier ID yields an expression that would evaluate without
  raising an error due to ID being unbound.  Otherwise, it returns #f.

Some examples.  Assume these are evaluated in an environment
containing the bindings for the identifiers specified by R5RS,
defined?, and nothing else.

  (let ((x 4)) (defined? x))
  => #t

This is because, if we make the substitution according to the
definition, we get (let ((x 4)) x); in this expression, we can
evaluate x without error.

  (let ((x 4)) (defined? y))
  => #f

This is because the substitution yields (let ((x 4)) y); since neither
the local environments nor the top-level R5RS environment provide a
binding for y, evaluating that variable reference would produce an error.

Suppose the following text were placed in a file:

  (define first (defined? nonny))
  (define nonny 'hey)
  (define second (defined? nonny))

Loading this file with R5RS's "load" function would give the variable
`first' the value #f, and the variable `second' the value #t, since
replacing the two defined? invocations with `nonny' would be an error
in the first case, but not in the second.  (I think R5RS doesn't
really spell this out, but it's consistent with the way Guile's `load'
works, and those of most other Scheme systems as well.)

A final example: suppose the following text were placed in a file:

  (define check (lambda () (defined? nonny)))
  (define first (check))
  (define nonny 'hey)
  (define second (check))

Loading this code should produce the same results as in the last
example, because substituting `nonny' into check's definition produces
something which won't evaluate the first time check is a called, and
will the second time.

Okay, so:

Should defined? be a function, or syntax?

Given its behavior in the `let' form above, it's clear that the value
of a defined? invocation depends on the lexically enclosing
environment.  But in Scheme, the call's environment is not made
available to functions we invoke --- this is the way the denotational
semantics are written in R5RS, and it's also more consistent with
lexical scoping.

So I think defined? should be syntax, not a function.  Its behavior
just isn't consistent with its being a function.

Note that Jost's suggested expansion of 

	(environment-bound? (car (the-environment)) <sym>)

is correct --- if defined? is a macro.  If it were a function, then
(the-environment) would return the function's lexical environment, not
the caller's.

Should defined? evaluate its argument?

Note that we can go either way with this, and still be consistent.
For example, if defined? did evaluate its argument, then you get
the following:

  (define (check id) (let ((x 3)) (defined? id)))
  (check 'x)
  => #t
  (check 'y)
  => #f
  (check 'car)
  => #t

However, I think this is a little troublesome, because it means that
you can't tell at compile-time what identifiers the program might
refer to.  Since the primary use of defined? is to handle variations
from one Scheme to another, I think defined? should not evaluate its
argument.  If you disagree, you must show why a function which accepts
a module or environment as an argument is not acceptable.

Should defined? be a memoizing macro, or not?

Clearly, in light of the third example (with the procedure `check'),
defined? cannot always be a memoizing macro.  However, there's nothing
to stop a clever compiler or a clever macro definition from expanding
(defined? nonny) to a constant, if it can prove that its value will
always be the same.  So I think Mikael and Marius are both right.

We certainly need a *function* that does something analogous, for
handling more dynamic situations.  However, this function *must*
require an environment or module as an argument, like module-defined?.
In the presence of multiple top-level environments, it's not
meaningful otherwise.

When people talk about (bound? ID) being a function, they're always
thinking about the "current environment".  I think this term is really
dangerous, and I'm trying to stamp it out.  When you're interacting
with Guile, there *is* a current environment --- it's the one the
definitions you type go into --- but it's a facet of the REPL, not of
the language.

Imagine that you have a server that receives Scheme expressions from
its clients and executes them.  The clients need to be separated from
each other --- they're each going to downloading their own useful
functions, and one client's definitions shouldn't interfere with the
others'.  So in this case, each client has its own "current
environment".  Each client is talking to its own instance of the REPL,
each REPL maintains its own "current environment", and it becomes
completely unclear what a bound? function would do.

In the long run, I'd like the Guile REPL to allow you to switch
top-level environments, so you can futz around as the user of a
module, then jump into the module and see what it thinks is going on,
tweak something, and then jump back out.  The REPL is really an
exploration tool.  (This is also why it should be its own module, and
why it should be completely separate from `load'.)

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]