Make functions and closures printable

Bug #596519 reported by Krzysztof Drewniak
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
SBCL
Triaged
Wishlist
Unassigned

Bug Description

I personally have a feature seen in other lisps (clisp) that I would like to see in sbcl, and that is to have functions and closures printable readably. The way clisp does it is with a variable custom:*print-closure* which, when set to t , allows anonymous functions to be printed readably. (*print-closure* is set to nil by with-standard-io-syntax) I would appreciate this feature in sbcl as it would make my life a lot easier.

Tags: feature
Revision history for this message
Nikodemus Siivola (nikodemus) wrote :

For interpreted functions and those with inline declarations this can be supported, but I don't think we want to support this for every function ever.

However, externalizable (dumpable into fasls) functions and closures are a different matter. Those would be very nice.

Changed in sbcl:
importance: Undecided → Wishlist
status: New → Triaged
Revision history for this message
Krzysztof Drewniak (krzysdrewniak) wrote :

I was thinking of a format that can be 'print'ed and 'read' back in. Compiled functions are what I was going for, but the more (supported) the merrier.

Revision history for this message
Nikodemus Siivola (nikodemus) wrote :

For your immediate needs, you can try:

(declaim (inline foo))
(defun foo (x) (1+ x))

(let ((form `(function ,(function-lambda-expression #'foo))))
  (funcall (eval form) 41)) => 42

That is, for inlined functions F-L-E will return a form you can combine with FUNCTION to produce a form that will evaluate to a function.

Revision history for this message
Douglas Katzman (dougk) wrote :

I suspect the bug reporter is confusing the issues of printing as _actually_ readable (preserving semantics of the closure with respect to what it captured), and printing in some way that reveals information about the closed-over values from a debugging perspective. Or at least I don't understand the intent.

It is most certainly not the case that Clisp can "readably" print any closure if you expect that to give back (from a subsequent READ) either the EQ object, or an object that at least shares the same closure variables. In case there is doubt about what I mean, let's be clear:

./clisp
(defun f (x) (list (lambda (y) (setq x y)) (lambda () x)))
(setq *funs* (f 1))
(setq *first* (read-from-string (let ((*print-closure* t)) (write-to-string (car *funs*)))))
(funcall *first* 42)
(print (funcall (second *funs*)))

So despite that I "readably" printed a closure, READ didn't do what I want. The above prints 1, not 42, because *FIRST* isn't actually the correct object after print and read.

In terms of how one can examine a closure, that's fairly simple, more or less what Nikodemus said:

(defun f (x y) (lambda (z) (+ (incf x) y z)))
* (function-lambda-expression (f 1 2)) =>
(LAMBDA (Z) (+ (INCF X) Y Z))
and if you want to see the closed-over values:
* (sb-vm:hexdump (f 1 2))
10018C0C70: 5030008300000335
10018C0C78: 0000000052B1DA80 = 693693760
10018C0C80: 0000000000000004 = 2
10018C0C88: 00000010018C0C6F = #<value cell 1 {10018C0C6F}>

In terms of constructing an arbitrary closure, I don't see that as a useful feature. To construct a closure you should call the function that constructs a closure. Moreover I don't know what it means to read back in a compiled function.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.