Debugging

Part of what sets Medley apart is a collection of dedicated functions and features to help us debug our programs.

FIX and ED

FIX line-number :

FIX lets us edit whatever we typed at a specific line number. It's useful when we want to make an immediate change.

Remember, we can refer to line numbers from any Exec. You might find it helpful to work with multiple smaller Execs, each focusing on a unique function of the larger program.

Back in the SEdit chapter, we learned about editing the definition of a function with (DF function-name), which Medley internally translates to (ED 'NAME '(FUNCTIONS FNS :DONTWAIT)). You don't need to use this expanded form, but it's good to know what goes on in the background.

(ED 'function-name)

While DF is for editing functions only, ED is the default editor function we can use to edit any File Manager object, including function definitions, variable values, property lists, or file package commands through SEdit.

The :DONTWAIT statement in DF’s call to ED lets SEdit return immediately, so the Exec doesn’t stall after starting an edit. This means you can continue working in the Exec while making edits in SEdit and changes take effect as you make them, without waiting for a completion command.

Try both DF and ED to edit a function. Does one of them let you switch to the Exec freely? Which one restricts you to SEdit?


INSPECT

At times, you will define variables containing values more complex than an atom. As our programs grow larger, we might want to take a quick look at the contents of these variables in an organized, tabular format without opening an editor like SEdit:

INSPECTallows us to do exactly that:

Select Inspect from the menu, and a window appears with two columns. The first column displays the numbered positions of the list items, and the second column displays the item in that position.

Notice how we use a quote (') before the variable name when we need to edit the definition of the variable. The system does not need to evaluate or understand the contents. It only needs to display the contents for us to make changes. Quote (') tells Lisp not to evaluate or "run" what follows. But when it comes to Inspect, we do want the system to understand the contents of the list so it can organize and display it neatly for us to review. No quotes for that operation!


BREAK

BREAK sets a breakpoint, letting you halt a function midway and inspect what might be going wrong in a dedicated Break Window. We can break and unbreak entire functions. This is handy when your function is huge, complex, and expensive to compute. We can also insert conditional breakpoints inside a function that breaks the function if certain criteria are met. This is useful when you want to ask questions about the flow of data through your program. Or when you want your program to stop its operation if (as opposed to printing the state and continuing).

If you're stuck in an infinite loop or just want to break out of the current running function, use Ctrl + B.

In a COND, use (ERROR "Something meaningful to print about the break") to trigger a break at a when you detect an error condition.

Let's define a function that prints out the factorial of any given number.

Factorials can be quite large. Suppose we want to limit the program so that only factorials of numbers no greater than 10 are printed. Pull up SEdit to edit the function factorial. Add another condition to the COND block:

((GREATERP X 10) (ERROR "This number is too large. X should be less than 11."))

This tells Medley to break the function if X is greater than 10.

Once the new function is compiled, try a few different values to test if it's working as intended.

For (factorial 11), or any number larger than 10, you should see the break window appear with your message:


TRACE

When your program is not acting as intended, the final output might not necessarily reveal what you need to fix. Being able to look at the internal calculations your functions are making and what output they generate at every step is a useful feature, and TRACE lets us do exactly that.

Let's trace our factorial function.

After we've traced a function with TRACE function-name, executing the function will open a Trace-Output window:

To stop tracing a function use UNTRACE:


ADVISE

As our programs grow more complex, we might not want to change the original code when we need to make a small, temporary change. ADVISE is a Medley feature to modify the behavior of a function by wrapping code "around" that function without having to edit it. It's also useful when you're working with someone else's code and want to preserve the integrity of their code.

The ADVISE function takes four arguments: (ADVISE function-name WHEN WHERE WHAT)

WHEN specifies when in the function call should the advised code be executed. Before the function, after the function, or should it replace the function's body entirely by executing around it? WHEN has three valid parameters: BEFORE, AFTER, AROUND

WHERE specifies where in a list of advice the current advise should be inserted. WHERE has two valid parameters: FIRST or TOP and LAST or BOTTOM or END. We can use NIL for WHERE unless we have multiple advises. If WHEN is AROUND, WHERE is considered NIL.

WHAT is the advised code to run.

Let's advise our factorial function to check if X is a string or a number:

Now, when we execute factorial, we see:

We can remove advice from a function using UNADVISE:

Try adding two ADVISEs to a new function. One should appear before and one after the function is executed.

!VALUE can be used to access the final value of the function. PROGN executes what follows sequentially. We'll learn more about it in the next chapter.

Last updated

Was this helpful?