Why do crosslinked defstructs cause stack overflows?

66 Views Asked by At

While playing around with graphs, I got a curious error I didn't quite understand. The code below reproduces the problem.

;; Define struct to store a node with links to other nodes.
(defstruct node properties links)

;; Make two nodes
(setf a (make-node :properties '(:name a))
      b (make-node :properties '(:name b)))

;; Create link from b to a. This works fine...
(push b (node-links a))

;; ... but this crosslink makes lisp chase its own tail for a while and then crash with a stack overflow.
(push a (node-links b))

I got the same result with SBCL and Clozure. Setting *print-length* to a manageable value did not work.

So my questions is: Why doesn't this code create the same kind of infinite printing loop as a circular list (i.e., no stack overflow and stoppable with Ctrl-C). Any input is appreciated.

Thanks, Paulo

1

There are 1 best solutions below

0
On BEST ANSWER

*print-length* controls the number of elements in a list. You're looking for *print-level*. This works fine for me.

(let ((*print-level* 3))
  (format t "~W~%" a))
;; Output: #S(NODE :PROPERTIES (:NAME A)
;;  :LINKS (#S(NODE :PROPERTIES # :LINKS #)))

Alternatively, you could use *print-circle* to detect cycles and print them in an even nicer way.

(let ((*print-circle* t))
  (format t "~W~%" a))
;; Output: #1=#S(NODE :PROPERTIES (:NAME A)
;;  :LINKS (#S(NODE :PROPERTIES (:NAME B) :LINKS (#1#))))

Here, it actually detects the cycle and prints #1#, a reference to the #1= to show that it's the same object.