Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easily adding trace statements to LISP dialects (without using do for example)

Tags:

clojure

lisp

When one is programming in an imperative programming languages such as Java one can conveniently add trace statements. For example:

for (int i=0; i<10; i++) {
  // do something
  // do something
  System.out.println("Some trace statement");
  // do something 
}

How does one accomplish this in a LISP dialect such as Clojure - for example say I wanted to add a trace just before recur:

(def fact
  (fn [n]
    (loop [cnt n acc 1]
       (if (zero? cnt)
            acc
          ;; say I want to add a trace here
          (recur (dec cnt) (* acc cnt))))))

Notes:

  1. The method should be relatively as simple as adding a line
  2. For example if I were to use a do block -- I have to reformat, make sure I close the brackets appropriately
like image 642
user1172468 Avatar asked Dec 01 '25 01:12

user1172468


2 Answers

Non-invasive tracing

Lisp environments generally provide interactive debugging environment and trace mechanisms. For example, in SBCL, you could use the trace macro: you don't even need to modify your code, like you did in your Java example.

For Clojure, look at the tools.trace library, or the following answer: clojure: adding a debug trace to every function in a namespace?

Custom functions and macros

See also the many answers to this question: Debugging in Clojure? Most of them involve nesting the expression you want to debug/trace inside another expression, like Chiron suggested.

I don't think that "I have to reformat and close the brackets appropriately" is a good argument; everytime you edit your program you have to deal with the syntax, or else you won't ever modify your code.

Paredit

I personally don't use I am now a happy user of Paredit. Your editor keep track of parens and brackets while you code, which is quite handy.

Reader macros

I you really don't want to nest your expression inside another one, I suppose you could write a reader macro so that you could annotate an expression with a debug statement, but this is overkill, imho (edit: this is what spyscope does, apparently; see NielsK's answer).

like image 63
coredump Avatar answered Dec 03 '25 18:12

coredump


The Spyscope library provides a simple option for putting in trace prints without having to change the original syntax, just in the way you (and many others) prefer.

spyscope.repl=> (take 20 (repeat #spy/p (+ 1 2 3)))
6
(6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6) 

There's also ways of including trace messages

spyscope.repl=> #spy/d ^{:marker "triple-add"} (+ 1 2 3)
spyscope.repl$eval3935.invoke(NO_SOURCE_FILE:1) triple-add (+ 1 2 3) => 6
6

and even (partial) stack traces

spyscope.repl=> (take 20 (repeat #spy/d ^{:fs 3} (+ 1 2 3)))
----------------------------------------
clojure.lang.Compiler.eval(Compiler.java:6477)
clojure.lang.Compiler.eval(Compiler.java:6511)
spyscope.repl$eval675.invoke(REPL:13) (+ 1 2 3) => 6
(6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6)
like image 45
NielsK Avatar answered Dec 03 '25 18:12

NielsK



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!