Exit is an error?

Exit is an error?

Prof_Stancescu
Enthusiast Enthusiast
3,250 Views
7 Replies
Message 1 of 8

Exit is an error?

Prof_Stancescu
Enthusiast
Enthusiast

Hi all,

 

This is a conceptual question based on an answer on this forum.

The beautiful answer was given by Tom Smith to the problem "quit vs. exit",

as quoted below:

 

"Jason, I tend to agree, it's a red flag that the code isn't structured in
the best way. The quit/exit amounts to a goto, and if it happens more than
once in a program you're on the road to spaghetti-code hell. That's exactly
the kind of thing that lisp in particular (and functional languages in
general) were designed to avoid.

 

Just as deeply nested ifs are a red flag...in fact any nesting of ifs at all
raises the question, why not cond instead? It will nearly always be clearer,
simpler, and easier to debug and maintain. A red flag doesn't mean *never*
do it no matter what, it just means it should alert your instincts that
there's *got* to be a better way.

 

A lot of autolispers seem to be real smart about the innards of Acad without
really comprehending lisp itself...more often than not, lisp functions tend
to written in an unreadable, basic-like procedural manner that denies the
elegance and simplicity that's possible in the language.

 

More autolispers should search out and read some of the many available
articles on programming style written by the gurus of Common Lisp. Nearly
all of this applies directly to AutoLISP. The "real" lisp people are
remarkably consistent about what is proper and good."

 

I am a low level programmer - just for reaching a target -, but I want to
learn how to create the right code, to the limit of my understanding,

being proud to use such an elegant environment as LISP.

 

In this spirit I wonder if I can avoid the brutal exit from a while cycle
as presented in the next routine.

 

Programare functionala.jpg

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Here is a while cycle where the user have to decide what happens

next by choosing OK or Cancel. For OK all is right, but for Cancel my

solution was to use the local variable OK_or_Cancel which gains its

value from the evaluating of the (start_dialog) function.

 

All is right but I receive an error because (exit) is considered an abnormal

way to suddenly leave the program. To accomplish the idea of a smooth

leaving of program now I use the *error* function with all the necessary

saving and restoration of the system variables, as well as eliminating the

error message itself.

 

Well, after all, I wonder if is there any other more proper solution instead of

my beginner one. How should I structure the illustrated routine to avoid

the (exit) function?

 

Constantin from Romania

 

PS Read about me here: https://grabcad.com/constantin.stancescu

0 Likes
Accepted solutions (1)
3,251 Views
7 Replies
Replies (7)
Message 2 of 8

phanaem
Collaborator
Collaborator

Hello Mr. Stancescu

 

I think something is missing from your code. What if OK_or_Cancel is 3?

It might be like this:

(if (= OK_or_Cancel 2)
  (exit)
  (progn
     (do_some_stuff)
  )
)

But it can be rewritten like:

(if (= OK_or_Cancel 3)
  (progn
    (do_some_stuff)
  )
  (princ "\nNothing to do. Canceled by the user.")
)

In short, you can check if OK is pressed and perform some task or return some value, different than nil. Otherwise, do nothing or return nil.

Message 3 of 8

dbroad
Mentor
Mentor

In addition to what @phanaem said, there is no reason to put the load and unload dialog statements in your loop. Put the load_dialog before and the unload_dialog after or in an error function if cancelled.

 

You also need to decide if cancel rises to an error.  OK should perform the actions. Cancel should not perform the actions. Your exit point is after the actions are done.  Suggestion: Use the cancel check to skip the actions and decide whether or not to exit the loop at the while function testexpr.

 

There is also the question of why you are using custom exit conditions anyway. The default return codes should work and should be used instead. Don't use the optional done_dialog argument unless the dialog control is unique in some way.

 

 

Architect, Registered NC, VA, SC, & GA.
0 Likes
Message 4 of 8

Prof_Stancescu
Enthusiast
Enthusiast

I reply here to both phanaem and dbroad - thank you!!!

In order to explain better the situation, now I insert a more extended code, after I learned from phanaem how to insert code

 

 

 

 

(defun ActionOrExit (/ DeWrong OK_or_Cancel)

  ;;
  ;;   Here is loaded the DCL file using (load_dialog ...)
  ;;

  (setq DeWrong 0)

;;Now the while cicle (while (and (not (NAMEValidationP)) (/= OK_or_Cancel 1)) (if (> DeWrong 0) (alert "Wrong Name - Try Again!") ) ;;Here is opened a dialog box definition (new_dialog . . . . . . . .) ;; ;;Here some actions ;; ;;Set actions for OK and Cancel (action_tile "accept" "(setq N$AME (get_tile \"Name\")) (done_dialog 2)" ) (action_tile "cancel" "(done_dialog 1)") ;;Present dialog box and wait for OK or Cancel (setq OK_or_Cancel (start_dialog)) ;;Increment for alert (setq DeWrong (1+ DeWrong)) ) (unload_dialog . . . . . . . . . . .) ;;Exit on Cancel (if (= OK_or_Cancel 1) (exit) ) )

 

 

The routine is a little subtle - because is made by me 🙂

 

As you can see, the while cycle condition is to both validate a name which is given as a global variable (N$AME) and to verify if the CANCEL is activated.

If the name is valid the routine does not make anything and can be forgotten.

If the name is not valid (not (NAMEValidationP)), in the first moment the OK_or_Cancel variable is nil, so the while cycle is started.

The integer variable DeWrong was setted on 0, then the alert is not activated and the dialog box is presented.

It asks for a new value of N$AME waiting for the user to type a new name.

If the user types a wrong name and clicks OK then the (not (NAMEValidationP)) is again True, while the OK_or_Cancel variable is still nil.

The while cycle is activated again, but the DeWrong variable was incremented, then the alert is presented.

Now the user types a right value for N$AME and clicks OK - the cycle is not reiterated because (not (NAMEValidationP)) is nil.

 

The routine ends with global variable N$AME validated.

 

Because the dialog box is endless repeated for successive wrong names, I thought that the user would be bored and would want an escape.

This is why I added CANCEL in the dialog box and I added the statement is_cancel = true; in the DCL.

The CANCEL sets the variable OK_or_Cancel on 1 due to done_dialog, so the last part of the routine activates (exit)

The user is bored, then the user taps on the key Ecs from the keyboard and all the program ends.

I assume this forced end because the program starts with this bloody asking for a value of  N$AME.

Using a definition for the user function *error* the program ends here smoothly, without presenting the quit/exit error message.

 

My friend phanaem is right - what means 3 on OK? Well I specially chosen absurd values, to emphasize the not default values for OK and CANCEL.

 

 

Based on this description of my routine I ask you again - How should I structure my routine to avoid the (exit) function?

In the Tom Smith's above mentioned answer about 'spaghetti-code hell" is no room for an intempestive exit in a well structured program, isn't it?

 

Yours,

Constantin

All the life we learn something...

0 Likes
Message 5 of 8

dbroad
Mentor
Mentor
Accepted solution
  1. Your need to load the dialog somewhere.
  2. The check for the cancel (if (= OK_....) is outside your loop so it doesn't serve any purpose except to deliberately cause an error.  The only return value you will ever have from this function is the return value of false from the if function. Using global variables works but isn't particularly good practice.
  3. If you are going to use dialog methods, the while loop should present the instructional/error messages in the dialog itself rather than either on the command line or an alert dialog. Alert dialogs are UI bad practice because they require a click through.
  4. If the user cancels the dialog, N$AME should be set to nil.
  5. The name of the function is obtuse.  It should be GetName or something related to what you are doing.
  6. Don't use done_dialog arguments to set up return values for accept and cancel. Use the default values for accept and cancel which are in the help files.
Architect, Registered NC, VA, SC, & GA.
0 Likes
Message 6 of 8

Prof_Stancescu
Enthusiast
Enthusiast

Your answer denotes a great knowledge of programming - thank you!

As I said, I use AutoLISP to solve a task, but I also want to learn good practices in this domain.

Your recommendations are wonderful, so I comment them for you to see what level of awkwardness I own at this time...

This would be useful for any other reader of this post, I suppose.

 

  1. Loading of dialog must be placed somewhere (else), but not in this function. My program has a lot of functions with many dialog boxes and I decided to load the DCL in this early stage function, and not in the main program. This is my mistake.
  2. The only return value of this function is a False coming from if. Using global variables is not a particularly good practice. You are right, but I did not intent to use any return value from this function, so I didn't care about. I thought that using a set of global variables well defined I can control better the different actions of functions.
  3. The dialog box must include instructional/error messages, and avoid alerts. This is generally a good recommendation, but here the alert in my function is used just to enforce user attention, after he already typed a new name in the dialog box and that name was (again) wrong, you see.
  4. If the user cancels the dialog, N$AME should be set to nil. Yes, from this point of view, but the whole program will end here anyway. On the other side, the N$AME already gets its value from an attribute taken from a block in the current dwg, and using this function the program tests its validity and asks the user for changing that value (which will change the attribute value as well) ONLY IF THAT VALUE IS WRONG.
  5. The function name is obtuse. This name was chosen by me only for this post in forum. The real name in my program is WrongName.
  6. Use the default values for accept and cancel. Yes, Sir - no comment...

At this point I declare again that I am a novice in (Auto)LISP, but I promise I shall try to respect your direction lines in my next program.

To make chaotic programs, without a good knowledge of the rules, creates great problems for using that program and debugging it.

 

Once again, I want to thank you and to consider your answer as a GOOD solution...

 

 

 

0 Likes
Message 7 of 8

dbroad
Mentor
Mentor

Glad to help.  Enjoy learning LISP and programming.

Architect, Registered NC, VA, SC, & GA.
0 Likes
Message 8 of 8

Prof_Stancescu
Enthusiast
Enthusiast

However, we didn't answer to the question: Exit is an error?

 

 

For now, I learned that an AutoLISP program must respect some important rules:

 

  • Use of cond instead of if as much as possible
  • Avoid exit because it pushes us to the "spaghetti-code hell"
  • Avoid alert because is not productive since it needs an extra click
  • Use default values for accept and cancel as much as possible
  • Create a lisible code from top-down, easy to read by anybody

There are many other rules, of course, but these seem to result from my post.

On the other hand, the error message "quit/exit abort" produced by exit can be trapped by using a specific definition of *error* function - preferable inserted in the C:MAIN function of program, with the symbolic name *error* as local argument.

 

All these considerations conduct us to a firm answer: Yes, exit is an error, but that error can be trapped.

 

 

Thank you for your lesson,

Constantin

0 Likes