[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: some questions



    From:           Dorab Patel <patel@UCLA-LOCUS.ARPA>
    
    1. I am still finding it difficult to use the debugger:
       For instance, if I have defined a function as
    
       (define (foo a b c d) 
               (let ((x (+ a b c d))) (debug)))
    
       Now I call it as (foo 6 7 8 9) and am put into the debugger.
    
       The first problem is that I can't locate the function "foo" on the
       stack.
    
Two problems: one is tail-recursion.  The way things are implemented,
values are discarded at the earliest possible opportunity.  Since
the values of the variables aren't logically needed to evaluate the
expression (DEBUG), they aren't there when you look at the stack,
and since no computation needs to be done after DEBUG returns, there
isn't even any frame where those values might be saved.

If the expression was (let ... (debug) t), then there would be another
stack frame around when DEBUG was called, so you should be able to
see the values of your variables using the inspector's E command.
(This is because the EVAL isn't smart enough to see that the expression
T doesn't use the bound variables.  TC is smarter, and would probably
discard the values sooner.)

Another problem is that the frame that DEBUG puts you at is the one
to which the value passed to RET will be thrown, and really has nothing
to do with the point at which DEBUG was called.  So you should call
BREAKPOINT instead of DEBUG, and then invoke DEBUG explicitly.  This
is why DEBUG is documented as a "command" and not as a more mundane
procedure - it is only intended to work as a command to a
read-eval-print loop.

       When I go down the stack, (and assuming I don't know the names of
       the arguments), is there some way of finding out what the values of
       arguments are? i.e. how do I distinguish arguments passed to
       procedures from other data on a stack?
    
No.  This would be nice but, again, it's hard to implement.

    2. How does one check for whether an identifier is bound or not in a
       given locale? env-lookup?
    
Yeah, I guess.  "Boundness" has real semantic problems and that's
why there is no released way to test for it.  The problem is being
pondered.

       What is the recommended way to create a new identifier inside
       another locale that is meant to be assigned to. I tried *define,
       but when I "(set (*value ...) 25)" it prints the "[Assigning ..]"
       message, which I'd rather avoid. Is there something like *lset?
    
There is a *LSET.  I'll see whether it is released; it ought to be.

       Actually, the more general question is, "Is it recommended to
       continue using env-lookup for such purposes, or is it
       discontinued?"
    
ENV-LOOKUP is being discontinued, but it will probably stick around until
the boundness problem is solved.

    3. Is there any way to get at the modification date of a file?
    
Nothing released or documented.  There may be useful foreign routines
lurking in the various implementations to deal with this, because
the U editor needs to do something.  I don't know much about this.
More info later.

    4. In general, is there some way a system embedded in T can catch some 
       system errors and correct them or print out its own error message?
       Instead of T printing out its error message and breaking?
    
There is an unreleased mechanism.  The source is in SIGNAL.T, and
an example of its use is in new versions of the TC source file
TRANSDUCE, in the procedure BATCH-COMPILE.  The mechanism isn't released
because it's inadequate and ungeneral.  I intend to redesign it.

    5. In T, macros are purely syntactic entities, in contrast to other
       Lisps, where they are procedures that do not evaluate their
       arguments, and are eval'ed twice. I believe that this is not
       adequately documented in the manual. This property causes
       counter-intuitive behavior and bad interactions with certain
       special forms. For example:
    
Agreed that the documentation is poor.  The next edition might do
a better job.  However, the manual makes very little attempt to document
how T differs from other Lisps, and someone unfamiliar with Lisp would
probably not find the situation as confusing, because the losing way
that most Lisps work wouldn't occur to them.

       (modify-location (foo x) (lambda (x y) ..)) does not work at all if
       "foo" is a macro.
    
I've been thinking about ways to make this work.  The problem is that
for a macro like MODIFY-LOCATION or SET to be able to look at subforms
and know whether or not they're macro invocations, they need to be
passed a syntax table, and there's no protocol for doing that at this
point.

       Naturally, (apply foo '(a)) bombs, unlike other Lisp dialects.
    
I could never figure out what this was supposed to mean.  I don't
know what Lisp Machine Lisp does with this, but I think it's an error
in Common Lisp and in NIL.  If what you want to do is macro expansion,
then there are INVOKE-MACRO-EXPANDER and MACRO-EXPAND.

    6. Very often, I want to write a function that maybe aborts by
       throw'ing out of a catch. However, for modularity reasons, the
       definition of the function is not within the lexical scope of the
       catch. Thus, I have to use "bind". I use the following template
       quite often and was wondering if you could comment on the style. Is
       there a better way of doing this?
    
            (let* ((errhandler '*bogusValue*) 
                   (tmp (catch pxer (bind ((errhandler (no-op pxer))) 
                                          (the-actual-code)))))
                  (if (and (pair? tmp) (eq? (car tmp) 'error))
                      (format (error-output) "it returned an error~%")
                      tmp))
            ....
        (define (the-actual-code)
                ....
                (errhandler `(error 23))
                ...)
    
I don't understand the your code (what good does BINDing the local
variable ERRHANDLER do, which is different from the global which is
called from THE-ACTUAL-CODE?), but I'll comment on what I think is
the intent:

- (CATCH FOO (BIND ((*FOO* FOO)) ...)) is a reasonable idiom, to my
  current way of thinking.
  
- You might try using two CATCHes:
  
      (catch value-is
        (catch abort (bind ((*abort* abort)) (value-is (the-actual-code))))
        (a-throw-to-abort-occurred))
             
At one time TC couldn't compile this double CATCH correctly, and I
don't remember whether the bug ever got fixed.  I think an extra NO-OP
or two will fix the problem if it still exists.  Also, I think that
the NO-OP in your example is unnecessary (in 2.7 at least).

In the future I expect there will be a condition/signalling mechanism
like in Common Lisp which should obviate most of the need for the
combination of CATCH and BIND.