A surprisingly large percentage of the errors made by Interlisp users are of the type that could be corrected by another LISP programmer without any information about the purpose of the program or expression in question, e.g., misspellings, certain kinds of parentheses errors, etc. To correct these types of errors we have implemented in Interlisp a DWIM facility, short for Do-What-I-Mean. DWIM is called automatically whenever an error occurs in the evaluation of an Interlisp expression. (Currently, DWIM only operates on unbound atoms and undefined function errors.) DWIM then proceeds to try to correct the mistake using the current context of computation plus information about what the user had previously been doing, (and what mistakes he had been making) as guides to the remedy of the error. If DWIM is able to make the correction, the computation continues as though no error had occurred. Otherwise, the procedure is the same as though DWIM had not intervened: a break occurs, or an unwind to the last {fn ERRORSET} ({PageRef Fn ERRORSET}). The following protocol illustrates the operation of DWIM. For example, suppose the user defines the factorial function {lisp (FACT N)} as follows: {lispcode ←DEFINEQ((FACT (LAMBDA (N) (COND ((ZEROP N9 1) ((T (ITIMS N (FACCT 8SUB1 N] (FACT) ←} Note that the definition of {lisp FACT} contains several mistakes: {fn ITIMES} and {lisp FACT} have been misspelled; the {lisp 9}{index 9 (instead of right parenthesis)} in {lisp N9} was intended to be a right parenthesis, but the shift key was not depressed; similarly, the {lisp 8}{index 8 (instead of left parenthesis)} in {lisp 8SUB1} was intended to be a left parenthesis; and finally, there is an extra left parenthesis in front of the {lisp T} that begins the final clause in the conditional. {lispcode ←PRETTYPRNT((FACCT] =PRETTYPRINT =FACT (FACT [LAMBDA (N) (COND ((ZEROP N9 1) ((T (ITIMS N (FACCT 8SUB1 N]) (FACT) ←} After defining {lisp FACT}, the user wishes to look at its definition using {lisp PRETTYPRINT}, which he unfortunately misspells. Since there is no function {lisp PRETTYPRNT} in the system, an undefined function {index undefined function error} error occurs, and DWIM is called. DWIM invokes its spelling corrector,{index spelling corrector} which searches a list of functions frequently used (by {it this} user) for the best possible match. Finding one that is extremely close, DWIM proceeds on the assumption that {lisp PRETTYPRNT} meant {lisp PRETTYPRINT}, notifies the user of this, and calls {fn PRETTYPRINT}. At this point, {lisp PRETTYPRINT} would normally print {lisp (FACCT NOT PRINTABLE)} and exit, since {lisp FACCT} has no definition. Note that this is {it not} an Interlisp error condition, so that DWIM would not be called as described above. However, it is obviously not what the user {it meant}. This sort of mistake is corrected by having {fn PRETTYPRINT} itself explicitly invoke the spelling corrector portion of DWIM whenever given a function with no {lisp EXPR} definition. Thus, with the aid of DWIM {fn PRETTYPRINT} is able to determine that the user wants to see the definition of the function {lisp FACT}, and proceeds accordingly. {lispcode ←FACT(3] N9 [IN FACT] -> N ) ? YES [IN FACT] (COND -- ((T --))) -> (COND -- (T --)) ITIMS [IN FACT] -> ITIMES FACCT [IN FACT] -> FACT 8SUB1 [IN FACT] -> ( SUB1 ? YES 6 ←PP FACT (FACT [LAMBDA (N) (COND ((ZEROP N) 1) (T (ITIMES N (FACT (SUB1 N]) FACT ←} {index -> (Printed by DWIM)} The user now calls {lisp FACT}. During its execution, five errors occur, and DWIM is called five times. At each point, the error is corrected, a message is printed describing the action taken, and the computation is allowed to continue as if no error had occurred. Following the last correction, 6 is printed, the value of {lisp (FACT 3)}. Finally, the user prettyprints the new, now correct, definition of {lisp FACT}. In this particular example, the user was shown operating in {index TRUSTING (DWIM mode)}{lisp TRUSTING} mode, which gives DWIM carte blanche for most corrections. The user can also operate in {index CAUTIOUS (DWIM mode)}{lisp CAUTIOUS} mode, in which case DWIM will inform him of intended corrections before they are made, and allow the user to approve or disapprove of them.{index approval (of DWIM corrections)} If DWIM was operating in {lisp CAUTIOUS} mode in the example above, it would proceed as follows: {lispcode ←FACT(3) N9 [IN FACT] -> N ) ? YES U.D.F. T [IN FACT] FIX? YES [IN FACT] (COND -- ((T --))) -> (COND -- (T --)) ITIMS [IN FACT] -> ITIMES ? ...YES FACCT [IN FACT] -> FACT ? ...YES 8SUB1 [IN FACT] -> ( SUB1 ? NO U.B.A. (8SUB1 BROKEN) :} {index -> (Printed by DWIM)} {index ... (Printed by DWIM)} For most corrections, if the user does not respond in a specified interval of time, DWIM automatically proceeds with the correction, so that the user need intervene only when he does not approve. Note that the user responded to the first, second, and fifth questions; DWIM responded for him on the third and fourth.{foot DWIM uses {fn ASKUSER} for its interactions with the user ({PageRef Fn ASKUSER}). Whenever an interaction is about to take place and the user has typed ahead, {fn ASKUSER} types several bells{index bells (printed by DWIM before an interaction)} to warn the user to stop typing, then clears and saves the input buffers, restoring them after the interaction is complete. Thus if the user has typed ahead before a DWIM interaction, DWIM will not confuse his type ahead with the answer to its question, nor will his typeahead be lost. The bells are printed by the function {fn PRINTBELLS}{index *PRIMARY* PRINTBELLS FN}, which can be advised or redefined for specialized applications, e.g. to flash the screen for a display terminal. }{comment endfootnote} A great deal of effort has gone into making DWIM "smart", and experience with a large number of users indicates that DWIM works very well; DWIM seldom fails to correct an error the user feels it should have, and almost never mistakenly corrects an error. However, it is important to note that even when DWIM {it is} wrong, no harm is done:{foot Except perhaps if DWIM's correction mistakenly caused a destructive computation to be initiated, and information was lost before the user could interrupt. We have not yet had such an incident occur. }{comment endfootnote} since an error had occurred, the user would have had to intervene anyway if DWIM took no action. Thus, if DWIM mistakenly corrects an error, the user simply interrupts or aborts the computation, {lisp UNDO}es the DWIM change using {index UNDO PACom}{pacom UNDO} ({PageRef PACom UNDO}), and makes the correction he would have had to make without DWIM. It is this benign quality of DWIM that makes it a valuable part of Interlisp. {index *PRIMARY* approval (of DWIM corrections)} {index *BEGIN* DWIM interaction with user} {FnDef {Name DWIM} {Args X} {Text Used to enable/disable DWIM. If {arg X} is the litatom {lisp C}, DWIM is enabled in {lisp CAUTIOUS} mode, so that DWIM will ask the user before making corrections.{index *PRIMARY* CAUTIOUS (DWIM mode)} If {arg X} is {lisp T}, DWIM is enabled in {lisp TRUSTING} mode, so DWIM will make most corrections automatically.{index *PRIMARY* TRUSTING (DWIM mode)} If {arg X} is {lisp NIL}, DWIM is disabled. Interlisp initially has DWIM enabled in {lisp CAUTIOUS} mode. {fn DWIM} returns {lisp CAUTIOUS}, {lisp TRUSTING} or {lisp NIL}, depending to what mode it has just been put into. {note foo! why not have it return the previous state, like almost every other state-changing function in Interlisp???} }} For corrections to expressions typed in by the user for immediate execution,{foot Typed into {index LISPX FN}{fn LISPX} (see {PageRef Fn LISPX}). }{comment endfootnote} DWIM always acts as though it were in {lisp TRUSTING} mode, i.e., no approval necessary. For certain types of corrections, e.g., run-on spelling corrections,{index run-on spelling corrections} 8-9 errors, etc., DWIM always acts like it was in {lisp CAUTIOUS} mode, and asks for approval.{index approval (of DWIM corrections)} In either case, DWIM always informs the user of its action as described below. {Begin SubSec Spelling Correction Protocol} {Title Spelling Correction Protocol} {Text {index *BEGIN* spelling correction protocol} One type of error that DWIM can correct is the misspelling of a function or a variable name. When an unbound litatom or undefined function error occurs, DWIM tries to correct the spelling of the bad litatom. If a litatom is found whose spelling is "close" to the offender, DWIM proceeds as follows: If the correction occurs in the typed-in expression, DWIM prints {lisp ={arg CORRECT-SPELLING}{CR}} and continues evaluating the expression. For example: {lispcode ←(SETQ FOO (IPLUSS 1 2)) =IPLUS 3} If the correction does not occur in type-in, DWIM prints{foot The appearance of {lisp ->}{index *PRIMARY* -> (Printed by DWIM)} is to call attention to the fact that the user's function will be or has been changed. }{comment endfootnote} {lispcode {arg BAD-SPELLING} [IN {arg FUNCTION-NAME}] -> {arg CORRECT-SPELLING}} Then, if DWIM is in {lisp TRUSTING} mode, it prints a carriage return, makes the correction, and continues the computation. If DWIM is in {lisp CAUTIOUS} mode, it prints a few spaces and {lisp ?}{index ? (Printed by DWIM)} and then wait for approval. The user then has six options: {Begin Numberedlist options} {Item Type {lisp Y}. DWIM types {lisp es}, and proceeds with the correction. } {Item Type {lisp N}. DWIM types {lisp o}, and does not make the correction. } {Item Type {lisp ↑}. DWIM does not make the correction, and furthermore guarantees that the error will not cause a break. } {Item Type control-E.{index control-E} For error correction, this has the same effect as typing {lisp N}. } {Item Do nothing. In this case DWIM waits for {index DWIMWAIT Var}{var DWIMWAIT} seconds, and if the user has not responded, DWIM will type {lisp ...}{index ... (Printed by DWIM)} followed by the default answer. The default on spelling corrections is determined by the value of the variable {index FIXSPELLDEFAULT Var}{var FIXSPELLDEFAULT}, whose top level value is initially {lisp Y}. } {Item Type space or carriage-return. In this case DWIM will wait indefinitely. This option is intended for those cases where the user wants to think about his answer, and wants to insure that DWIM does not get "impatient" and answer for him. } {End Numberedlist options} The procedure for spelling correction on other than Interlisp errors is analogous. If the correction is being handled as type-in, DWIM prints {lisp =}{index = (Printed by DWIM)} followed by the correct spelling, and returns it to the function that called DWIM. Otherwise, DWIM prints the incorrect spelling, followed by the correct spelling. Then, if DWIM if in {lisp TRUSTING} mode, DWIM prints a carriage-return and returns the correct spelling. Otherwise, DWIM prints a few spaces and a {lisp ?}{index ? (Printed by DWIM)} and waits for approval. The user can then respond with {lisp Y}, {lisp N}, control-E, space, carriage return, or do nothing as described above. Note that the spelling corrector itself is not {fn ERRORSET} protected like the DWIM error correction routines. Therefore, typing {lisp N} and typing {index control-E}control-E may have different effects when the spelling corrector is called directly. The former simply instructs the spelling corrector to return {lisp NIL}, and lets the calling function decide what to do next; the latter causes an error which unwinds to the last {fn ERRORSET}, however far back that may be. {Begin Note} Date: 24 OCT 1978 2318-PDT From: TEITELMAN Subject: lower case spelling correction at henry's request, dwim will now ask for approval on spelling correction involving simple case shiftts when the correction is to a user program. it used to always perform the correction without any approval or notification. {End Note} {index *END* spelling correction protocol} }{End SubSec Spelling Correction Protocol} {Begin SubSec Parentheses Errors Protocol} {Title Parentheses Errors Protocol} {Text When an unbound litatom or undefined error occurs, and the offending litatom contains {lisp 8} or {lisp 9}, DWIM tries to correct errors caused by typing {lisp 8}{index *PRIMARY* 8 (instead of left parenthesis)} for left parenthesis and {lisp 9}{index *PRIMARY* 9 (instead of right parenthesis)} for right parenthesis.{foot Actually, DWIM uses the value of the variables {var LPARKEY} and {var RPARKEY}{index LPARKEY Var}{index RPARKEY Var} to determine the corresponding lower case character for left and right parentheses. {var LPARKEY} and {var RPARKEY} are initially {lisp 8} and {lisp 9} respectively, but they can be reset for other keyboard layouts.{index keyboard layouts}, e.g., on some terminals left parenthesis is over {lisp 9}, and right parenthesis is over {lisp 0}. }{comment endfootnote} In these cases, the interaction with the user is similar to that for spelling correction. If the error occurs in type-in, DWIM types {lisp ={arg CORRECTION}{CR}}, and continues evaluating the expression.{index = (Printed by DWIM)} For example: {lispcode ←(SETQ FOO 8IPLUS 1 2] = ( IPLUS 3} If the correction does not occur in type-in, DWIM prints{index -> (Printed by DWIM)} {lispcode {arg BAD-ATOM} [IN {arg FUNCTION-NAME}] -> {arg CORRECTION} ?} and then waits for approval. The user then has the same six options as for spelling correction, except the waiting time is 3*{var DWIMWAIT}{index DWIMWAIT Var} seconds. If the user types {lisp Y}, DWIM then operates as if it were in {lisp TRUSTING} mode, i.e., it makes the correction and prints its message. }{End SubSec Parentheses Errors Protocol} {Begin SubSec U.D.F. T Errors Protocol} {Title U.D.F. T Errors Protocol} {Text {Tag DWIMU.D.F.T} When an undefined function error occurs, and the offending function is {lisp T}, DWIM tries to correct certain types of parentheses errors involving a {lisp T} clause in a conditional. DWIM recognizes errors of the following forms: {Begin Table errors of the form} {COLUMN} {COLUMN} {First {lisp (COND --) (T --)}} {Next The {lisp T} clause appears outside and immediately following the {fn COND}.} {First {lisp (COND -- (-- & (T --)))}} {Next The {lisp T} clause appears inside a previous clause.} {First {lisp (COND -- ((T --)))}} {Next The {lisp T} clause has an extra pair of parentheses around it.} {End Table errors of the form} For {lisp U.D.F. T}{index U.D.F. T (Printed by DWIM)} errors that are not one of these three types, DWIM takes no corrective action at all, and the error will occur. If the error occurs in type-in, DWIM simply types {lisp T FIXED} and makes the correction.{index T FIXED (Printed by DWIM)} Otherwise if DWIM is in {lisp TRUSTING} mode, DWIM makes the correction and prints the message: {lispcode [IN {arg FUNCTION-NAME}] {bracket BAD-COND} -> {bracket CORRECTED-COND}} If DWIM is in {lisp CAUTIOUS} mode, DWIM prints {lispcode U.D.F. T [IN {arg FUNCTION-NAME}] FIX?} and waits for approval. The user then has the same options as for spelling corrections and parenthesis errors. If the user types {lisp Y} or defaults, DWIM makes the correction and prints its message. {note is all this correct?? this doesn't agree with the example at the beginning of the chapter} Having made the correction, DWIM must then decide how to proceed with the computation. In the first case, {lisp (COND --) (T --)}, DWIM cannot know whether the {lisp T} clause would have been executed if it had been inside of the {lisp COND}. Therefore DWIM asks the user {lisp CONTINUE WITH T CLAUSE}{index CONTINUE WITH T CLAUSE (Printed by DWIM)} (with a default of {lisp YES}). If the user types {lisp N}, DWIM continues with the form after the {lisp COND}, i.e., the form that originally followed the {lisp T} clause. In the second case, {lisp (COND -- (-- & (T --)))}, DWIM has a different problem. After moving the {lisp T} clause to its proper place, DWIM must return as the value of {lisp &} as the value of the {lisp COND}. Since this value is no longer around, DWIM asks the user, {lisp OK TO REEVALUATE}{index OK TO REEVALUATE (Printed by DWIM)} and then prints the expression corresponding to {lisp &}. If the user types {lisp Y}, or defaults, DWIM continues by reevaluating {lisp &}, otherwise DWIM aborts, and a {lisp U.D.F. T} error will then occur (even though the {lisp COND} has in fact been fixed).{foot If DWIM can determine for itself that the form can safely be reevaluated, it does not consult the user before reevaluating. DWIM can do this if the form is atomic, or {fn CAR} of the form is a member of the list {index OKREEVALST Var}{var OKREEVALST}, and each of the arguments can safely be reevaluated. For example, {lisp (SETQ X (CONS (IPLUS Y Z) W))} is safe to reevaluate because {fn SETQ}, {fn CONS}, and {fn IPLUS} are all on {var OKREEVALST}. }{comment endfootnote} In the third case, {lisp (COND -- ((T --)))}, there is no problem with continuation, so no further interaction is necessary. }{End SubSec U.D.F. T Errors Protocol} {index *END* DWIM interaction with user} {index *END* *PRIMARY* approval (of DWIM corrections)}