TweetFollow Us on Twitter

Debugging in Lisp
Volume Number:2
Issue Number:6
Column Tag:AI Applications

Debugging in Lisp

By Andy Cohen, AI Contributing Editor

[Since the last discussion on Macscheme, Semantic Microsystems has updated their product. They are currently shipping version 1.1 which includes access via Macscheme primitives to the Quickdraw routines in ROM. Version 1.1 is also compatible with The Mac+ and HFS. Registered owners of earlier versions can update by sending $20 and their original MacScheme disk to Sematic Microsystems. Additionally, Semantic Microsystems announced the first part of a new development system called Macscheme+Toolsmith. This product includes Pascal-like access to the Toolbox routines in ROM from Macscheme. It also allows the programmer to design an apparently free standing application without the MacScheme environment. A minimized run time system will be required, however, the application and the run time system may be sold without royalties to Sematic Microsystems. Macscheme+ToolSmith will be sold at $250, while Macscheme is still $125 (Which makes us wonder, why $20 for the update?!). We've been pleased with the MacScheme product and the developers' responses to help requests. This month we feature a simple description on using the debugger in MacScheme. - Andy Cohen, A.I. Contributing Editor]

Debugging in Lisp

by

Anne Hartheimer

Semantic Microsystems, Inc.

Most Lisp systems include good debugging tools, in part because Lisp has traditionally been an interactive, interpreted language. I am going to explain the sorts of debugging tools that are typically provided by Lisp systems, how to take advantage of them and, in particular, how to use the debugging aids provided by the MacScheme Lisp system.

What kinds of debugging help can a system give you? For starters, it can tell you that an error exists. It can also help you find the error that it has told you about. Of course, it can't detect all errors. For example, only you can tell if your program is doing the right thing or generating the output you desire. However, if you know that a program produces the wrong output, or at least questionable output, a debugging tool can help you figure out why. Once you have located the source of the error, Lisp debugging tools can help you fix your buggy program and test it conveniently.

There are several standard debugging tools. Compile-time and run-time error checks discover errors. Inspectors let you examine the scene of the crime. If you suspect that there might be something wrong with a particular procedure, then tracers, steppers, and the ability to insert breakpoints in a program can help you see what is happening as the program is executed. Lastly, editors and incremental compilers can make it easy to modify small parts of your program and move on to the next bug. (One of the reasons Lisp interpreters are popular is that they give you incremental compilation for free.)

MacScheme has all of these tools except a stepper. I'll be using MacScheme in the examples that follow.

Compile-time Checks

Error checks can be performed at compile-time or at run-time. A compiler checks for illegal syntax and some kinds of domain errors. (Compile-time checks for domain errors are usually called type-checks.) In MacScheme, when a compile-time error is detected, it is reported and the system performs a reset. For example, if is a reserved word, so it is not legal syntax to assign to it:

>>> (set! if 3)

ERROR:  Keywords of special forms may
        not be used as variables
(set! if 3)

If a procedure is known to the compiler, then the compiler can make sure it is given the right number of arguments. This is an example of a compile-time domain check.

>>> (cons 3 4 5)

ERROR:  Wrong number of arguments to
        integrable procedure
(cons 3 4 5)

MacScheme, like most Lisps, performs very few compile-time domain checks. This is because Lisp associates types with run-time objects instead of with variables, so compile-time checking is not possible in general. This puts Lisp at a disadvantage compared to Pascal because run-time domain checks are performed only on the pieces of code that are actually executed. If you have a program that contains several branches, you can not be sure that it is free of domain errors until you test each of the branches. In Pascal, on the other hand, many domain errors are caught at compile-time, placing less of a burden on testing. Lisp must make up for this disadvantage by having excellent run-time domain checking.

Run-time Checks

Interpretive Lisp systems excel at run-time error checks. An interpreter will detect undefined variables and domain errors such as passing the wrong number of arguments to a procedure, using a non-procedural object as a procedure, attempting to add two strings, etc. Most systems interrupt execution, print a helpful error message on the screen, and place you inside an inspector so you can examine the context of the error. This is a far cry from a core dump or a bomb window.

The following example is from page 72 of The Little Lisper. vec+ is a function which takes two vectors and returns the vector that is their sum. (The Little Lisper represents vectors as lists instead of using Scheme's built-in vector data type.)

>>> (define vec+
      (lambda (vec1 vec2)
        (cond
         ((null? vec1) ())
         (t (cons
             (+ (car vec1) (car vec2))
             (vec+ (cdr vec1)
                   (cdr vec2)))))))
vec+
>>> (vec+ '(3 4 5) '(1 1 1))
(4 5 6)
>>> (vec+ '(1 2 3 4) '(0 2))

ERROR:  Bad argument to cdr
()

Entering debugger.  Enter ? for help.

Oops! Let's track down the cause of this error message. We first ask for the name of the procedure in which the error occured.

debug:> i ; "i" means "identify procedure"
vec+

So the error occurred in our vec+ routine. Let's look at the code for vec+.

debug:> c ; "c" means "show code"
(lambda (vec1 vec2)
  (cond ((null? vec1) ())
        (t
         (cons (+ (car vec1) (car vec2))
               (vec+ (cdr vec1)
                     (cdr vec2))))))

Since the error message told us that cdr was passed a bad argument of (), we should think about the calls to cdr in vec+. (In Scheme, cdr is defined only on non-null lists. When applied to a non-null list, it returns a list consisting of everything in its argument except the first element.) Let's check out the values of vec1 and vec2 since these are the two possible arguments to cdr that could be causing the error.

debug:> a ; "a" means "all variables"

vec1 = (3 4)
vec2 = ()

So vec2 is the problem. Looking more carefully at the code, we can see that we're testing to see if vec1 is null but not vec2. We need to add a test for vec2.

Inside the MacScheme debugger you can find out the name of the procedure in which the error occurred, the arguments that were passed to that procedure, the variables accessible at that point and their values, and the chain of procedures awaiting results. You can move back to the context of any procedure awaiting a result and look at the variables that are accessible to that procedure. You can evaluate arbitrary expressions in the environment of any of the procedures along the chain. You can modify arguments passed to those procedures, and then resume computation. You can modify the values of variables and procedure definitions within the environments of any of the procedures along the chain and resume computation. In addition, you can exit the MacScheme debugger, perform some other computation, and then decide that you want to do additional inspecting of the last bug you encountered. You can reenter the debugger and be back in the state of the most recent error.

Breakpoints, Tracers, and Steppers

Let's shift gears now, and consider the following situation. Suppose your program is producing incorrect output. The traditional approach is to insert print statements in it. The problem with inserting print statements is that it is difficult to know what information to print, and if you print too much the information is unwieldy. It is better to insert breakpoints into your program. In MacScheme, when (break) is evaluated, execution is interrupted and you are placed in the MacScheme debugger. You can now use the full power of the inspector to examine the context of the breakpoint. Instead of having to decide at compile-time what features will be of interest (as you had to do when you debugged by inserting print statements), you get to decide interactively what to look at. For breakpoints to be really useful, your inspector must let you resume the computation. Then you can move on to the next breakpoint that you have set if you decide that things look just fine in the context of the first breakpoint.

It's also useful to be able to induce a break manually to snoop around in computations that seem to be taking a long time or acting weirdly. MacScheme lets you do so by selecting Break from the menu. This interrupts a computation and places you in the MacScheme debugger. When you have finished looking around, you can type an "r" to resume the computation.

If you suspect that a particular routine embedded in your program is producing bad outputs or receiving bad inputs, a convenient way to test your hypothesis is to trace the routine. Whenever a traced routine is called, the MacScheme tracer prints its name and the arguments it was passed. When the routine returns, its name and the value it returns are printed.

>>> (define (fact n)
      (if (zero? n)
          0
          (* n (fact (- n 1)))))
fact
>>> (fact 3)
0
>>> (trace fact)
#!true
>>> (fact 3)
Computing (#<PROCEDURE fact> 3)
 Computing (#<PROCEDURE fact> 2)
  Computing (#<PROCEDURE fact> 1)
   Computing (#<PROCEDURE fact> 0)
   (#<PROCEDURE fact> 0) --> 0
  (#<PROCEDURE fact> 1) --> 0
 (#<PROCEDURE fact> 2) --> 0
(#<PROCEDURE fact> 3) --> 0
0
>>>

Here we see that the recursive calls are passing the correct arguments, but the wrong results are returned.

Once you have determined the problem to be a routine that is receiving good inputs and producing bad outputs, and you have little idea why, you might want to step through the procedure to see what is going wrong. A stepper allows you to view the evaluation of each subexpression of a routine as it is happening. Some steppers let you use the full power of the inspector at each step along the way. You can view a stepper as the ability to insert breakpoints automatically between every expression of your program. A stepper is the one tool I've mentioned that MacScheme doesn't have. To explain why MacScheme does not have a stepper, we must look at the interaction between compiling and debugging.

Interpreters and Compilers

Interpreters are best for development and debugging because they work with the source code, which programmers understand. Compilers turn nice, readable source code into the gobbledygook of machine language--but that machine language sure runs fast. Most Lisp compilers also give up some run-time checking in order to get more speed. One of the main advantages of special-purpose Lisp machines over comparably priced conventional computers is that Lisp machines have special hardware to perform run-time checks very quickly.

MacScheme, like Smalltalk-80, uses a compromise. It compiles to byte code--the machine language for a hypothetical Lisp machine--and then interprets the byte code. This approach gives most of the speed of compiled code together with most of the nice debugging associated with interpreted code. The byte code is also more compact than either native code or source code if the source code is discarded after compilation, but MacScheme normally keeps the source code around to aid in debugging.

Though MacScheme keeps the source code for each user-defined procedure, it does not try to remember the correspondence between individual byte code instructions and source code subexpressions. There just isn't enough memory on a Macintosh to maintain the large tables that would be necessary, which is why MacScheme doesn't have a stepper. Smalltalk-80, on the other hand, does something very clever. Whenever you run its stepper, Smalltalk constructs those tables incrementally, by de-compiling the byte code if necessary.

Texas Instruments' PC Scheme for the IBM PC and TI Professional also uses byte code. Compared to MacScheme, PC Scheme emphasizes speed at some cost to debugging. For example, PC Scheme normally does not retain source code. Because the PC is slower than the Mac and can address less memory, this is a reasonable engineering compromise.

Conclusions

No set of debugging tools can make debugging easy, but they certainly can make it easier, faster, and more fun. We've looked at the help provided by compile-time and run-time error checking, inspectors, breakpoints, tracers and steppers. These tools were first developed for Lisp systems in the 1960's, yet are still hard to find in other languages. Lisp's supportive programming environment is one of the main reasons why so many Lisp programmers are fanatical about their favorite language.

References

Daniel Friedman and Matthias Felleisen. The Little Lisper. Second Edition. Chicago: Science Research Associates, Inc. (ISBN 0-574-21955-2) 1986.

Adele Goldberg. Smalltalk-80: The Interactive Programming Environment. Menlo-Park: Addison-Wesley Publishing Co. 1984.

How to Turn Off the Debugger

If the automatic placement into the debugger is confusing or it is just a pain to you, try the following procedure sent to us by the folks at Semantic Microsystems:

;;; (debugger #!false) turns off the debugger.
;;; (debugger #!true) turns on the debugger.
;;; (debugger) returns #!true if the debugger is on,
;;; otherwise it returns #!false

(define debugger
 (let ((state #!true)
 (real-debug debug)
 (fake-debug (lambda args (reset))))
 (lambda flags        ; flags is a list of arguments
 (if (null? flags)
 state            ; no arguments
 (begin
 (set! debug (if (car flags) real-debug fake-debug))
 (set! state (car flags))
 state)))))

Corrections

A couple of typos from the last column on MacScheme were identified by the author. The first was in one of the code samples using sort. The following is the correct code:

>>> (sort '("Even" "mathematicians" "are" "accustomed" "to" "treating" 
"functions" "as" "underprivileged" "objects")
 (lambda (x y)
 (or (<? (string-length x) (string-length y))
 (string<? x y))))

("as ...)

The second error was in the code defining the procedure make-counter. The code should have included the argument n as follows:

(define make-counter
 (lambda (n)
 (lambda ()
 (set! n (+ n 1))
 n)))

There was also an error in the third reference. The reference was supposed to read Joseph Stoy's Denotational Semantics of Programming Languages. Our thanks to Will Clinger of the Tektronix Computer Research Laboratory (and also of Semantic Microsystems).

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Warcher Defenders (Games)
Warcher Defenders 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Warcher Defenders is a tiny castle defense action RPG with a very unique and challenging gameplay! Join this epic archery... | Read more »
The best value players on UEFA EURO 2016...
With Euro 2016 kicking off on June 10th, time's running out to pick your 15-man fantasy squad in UEFA EURO 2016 Fantasy. [Read more] | Read more »
How to become the ultimate robot warrior...
Chrono Strike is a delightfully immersive beat ‘em up with a sense of humor (any game with a good Sims reference gets points in my book). [Read more] | Read more »
Tips and tricks to get a higher score in...
Snow Roll is a devilish endless runner very much in the vein of Flappy Bird. It revels in its dastardly level of difficulty, and doesn’t really care how angry you get at it as it knows you’ll keep coming back for more. [Read more] | Read more »
How to win big in Slots Deluxe
Cheating while gambling is illegal and morally wrong, and in some parts of the world it leads to men with names like Vinnie "Six Knuckles" Manchenzo beating you to a pulp in a dark alley. [Read more] | Read more »
How to take over the world in Dictator 2
Running a country isn't easy - especially when you're a dictator who wants to take over the world and crush everyone in your path while you do it. [Read more] | Read more »
Tips and tricks to get a higher score in...
Tank.iois - you guessed it! - another multiplayer arena battler likeAgar.io and Slither.io. It does differentiate itself by putting you in a tiny tank though, so it's not exactly the same. To help you get that all-important high score, we've got a... | Read more »
How to unlock characters in One Tap Tenn...
As the title suggests, One Tap Tennis requires only a single tap to play its particular brand of tennis, and rewards you with a ton of unlockable characters if you perform well. Fortunately for you, we at 148Apps have got a few tips and tricks to... | Read more »
Grab it now: Game Craft’s Legend of War...
The real time strategy game is now available for you to sink your teeth into, through the App Store and Google Play. Combining elements of skill, strategy and empire building, Legend of War is a real gamers’ game. [Read more] | Read more »
Skateboard Party 3 ft. Greg Lutzka (Gam...
Skateboard Party 3 ft. Greg Lutzka 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Skateboard Party is back! This third edition of the popular sports franchise features professional skater... | Read more »

Price Scanner via MacPrices.net

Dell Launches World’s First 17-inch Convertib...
Apple and other laptop makers have stepped away from the 17-inch display laptop form factor, frustrating users who liked the vast expanse of screen real estate. Having used a 17-inch PowerBook for... Read more
12-inch 1.2GHz Space Gray Retina MacBook avai...
Amazon has the new 12″ 1.2GHz/512GB Space Gray Retina MacBook (Apple model #MLH82LL/A) available for $1549 including free shipping. Their price is $50 off MSRP. B&H has the 12″ 1.2GHz/512GB Space... Read more
Apple refurbished Mac Pros available for up t...
Apple has Certified Refurbished Mac Pros available for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The following... Read more
Enterprise Workers Pick Technology Over Perks...
New Adobe study shows surprising attitudes about office jobs and where the future of work is heading. Adobe has released survey findings revealing that a surprising 70 percent of U.S. office workers... Read more
Memorial Day Weekend Sale: $50-$100 off 11-in...
B&H Photo has 13″ and 11″ MacBook Airs with 256GB SSDs on sale for $50-$100 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 11″ 1.6GHz/256GB MacBook Air: $999 $100 off MSRP... Read more
Memorial Day Weekend Sales: Apple MacBook Pro...
B&H Photo has 13″ and 15″ Retina MacBook Pros on sale for up to $210 off MSRP. Shipping is free, and B&H charges NY tax only: - 15″ 2.2GHz Retina MacBook Pro: $1799 $200 off MSRP - 15″ 2.5GHz... Read more
Memorial Day Weekend Sales: Apple iMacs and M...
Take up to $150 off the price of a new iMac or Mac mini at the following Apple resellers this Memorial Day weekend: iMacs: B&H Photo has 21″ and 27″ iMacs on sale for up to $150 off MSRP... Read more
Apple refurbished Retina MacBook Pros availab...
Apple has Certified Refurbished 2015 13″ and 15″ Retina MacBook Pros available for up to $380 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free... Read more
Apple refurbished 11-inch MacBook Airs availa...
Apple has Certified Refurbished 11″ MacBook Airs (the latest models), available for up to $170 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is... Read more
Goal Zero and OtterBox Partner to Expand iPh...
Goal Zero, specialists in portable power, have announced a partnership with OtterBox, brand smartphone case protection, to offer the Slide and Slide Plus Batteries as modules compatible with the new... Read more

Jobs Board

*Apple* Architect - AECOM (United States)
**Requisition/Vacancy No.** 132759BR **Position Title** Apple Architect **Job Category** Information Technology **Business Line** Government **Country** United Read more
*Apple* Project Engineer - Smart Source Inc...
SmartSource is in need of an Apple Project Engineer for a 12 month contract opportunity in Pittsburg, PA. Role: Apple Project Engineer Location: Pittsburg, PA Read more
Senior *Apple* Engineer - Signature Technol...
One-year contract for an Apple consultant. The resource should be an Apple Certified Technical Coordinator or get the certification within 60 days of starting Read more
Senior *Apple* Engineer - Signature Technol...
One-year contract for an Apple consultant. The resource should be an Apple Certified Technical Coordinator or get the certification within 60 days of starting Read more
*Apple* Architect - AECOM (United States)
**Requisition/Vacancy No.** 132759BR **Position Title** Apple Architect **Job Category** Information Technology **Business Line** Government **Country** United Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.