Re: How to save lists containing functions to disk



"javuchi" <javuchi@xxxxxxxxx> writes:

> Doing this in CMUCL:
>
> (defun pp (x) (* x 2))
> (compile 'pp)
> (list 'a 'b (symbol-function 'pp))
>
> I obtain this:
>
> (A B #<Function PP {58A94879}>)
>
> Is there any way to save the list to a file, including the compiled
> function, and then, in another session, reload and use that function?
> Thanks.

There's no standard API to save compiled functions.
Perhaps your implementation has something for it?

You can try to save the source of the function.

Note that for functions enclosed in a closures, it may be hard to
retrieve a meaningful source, but let's ignore this issue for now.

You could try to use the standard function FUNCTION-LAMBDA-EXPRESSION.
Unfortunately, implementations are allowed to return NIL, rendering
FUNCTION-LAMBDA-EXPRESSION useless when you need it (Murphy's Law).

So the only portable way to do it, is to save yourself the function
definition. For functions defined with DEFUN, you could shadow DEFUN,
and write your own macro that would save the source of the function on
the property list of the function name symbol. But a smarter way
needs to be implemented anyways to handle (SETF name) and anonymous
functions built with LAMBDA.

Something like:


(defparameter *function-sources* (make-hash-table :test (function equal)))

(shadow '(defun defmacro lambda))

(cl:defmacro defmacro (name args &body body)
`(progn
(compile (cl:defmacro ,name ,args ,@body))
(setf (gethash (fdefinition ',name) *function-sources*)
'(defmacro ,name ,args ,@body))
',name))

(cl:defmacro defun (name args &body body)
`(progn
(compile (cl:defun ,name ,args ,@body))
(setf (gethash (fdefinition ',name) *function-sources*)
'(defun ,name ,args ,@body))
',name))

(cl:defmacro lambda (args &body body)
(let ((fun (gensym)))
`(let ((,fun (compile nil (cl:lambda ,args ,@body))))
(setf (gethash ,fun *function-sources*) '(lambda ,args ,@body))
,fun)))


(defun print-function (fun &optional (stream t))
(print (or (gethash fun *function-sources*) fun) stream))


(Note that I compile the functions immediately, which may not be the
normal behavior when an interpreter is available.)

So now you can write:

(defun f (x) (1+ x))
(defmacro g (x) `(print (1+ ,x)))
(setf c (lambda (z) (+ (sqrt -1) z)))

(dolist (x (list (function f) (symbol-function 'g) c))
(print-function x))

-->
(DEFUN F (X) (1+ X))
(DEFMACRO G (X) `(PRINT (1+ ,X)))
(LAMBDA (Z) (+ (SQRT -1) Z))


Now, for the closures. In the case of a single function in a closure,
we could extend the above macros to get hold of the environment, and
try to reproduce it, printing (LET (...) (DEFUN ...)).

But environments are opaque in Common Lisp, so you cannot do anything
portably. If you have the good taste of using an implementation that
gives an API to access the environment, then you could generate
additionnal functions in the closure to let you retrieve the values in
the closure at run-time, when you print the function; something like:


(cl:defmacro defun (&environment env name args &body body)
`(progn
(compile (cl:defun ,name ,args ,@body))
(setf (gethash (fdefinition ',name) *function-sources*)
(lambda ()
(list 'let
,(cons 'list
(%map-env ; IMPLEMENTATION SPECIFIC!
(lambda (var)
(list 'list (list 'quote var) var))
env))
'(defun ,name ,args ,@body))))
',name))

;; And do the same for defmacro and lambda...

which would generate:

[93]> (defmacro ee1 (&environment env form)
`(print (macroexpand-1 ',form ',env)))
EE1
[94]> (let ((x 1) (y 2)) (ee1 (defun f (x) (1+ x))))

(PROGN (COMPILE (COMMON-LISP:DEFUN F (X) (1+ X)))
(SETF (GETHASH (FDEFINITION 'F) *FUNCTION-SOURCES*)
(LAMBDA NIL (LIST 'LET (LIST (LIST 'Y Y) (LIST 'X X)) '(DEFUN F (X) (1+ X)))))
'F)
(PROGN (COMPILE (COMMON-LISP:DEFUN F (X) (1+ X)))
(SETF (GETHASH (FDEFINITION 'F) *FUNCTION-SOURCES*)
(LAMBDA NIL (LIST 'LET (LIST (LIST 'Y Y) (LIST 'X X)) '(DEFUN F (X) (1+ X)))))
'F)
[95]>

So instead of keeping directly the source form to print for each
function, we keep a closure that will generate this source form with
the current state of the closure when it'll be called:


(defun print-function (fun &optional (stream t))
(let ((clo (gethash fun *function-sources*)))
(print (if clo (funcall clo) fun) stream)))

Not portable, but this can be manageable for a couple of implementations.

Now what will be harder, is to be able to print something meaningful
for these functions:

(let ((x 1))
(defun incx (i) (incf x i))
(defun resetx () (setf x 0))
(defparameter getx (lambda (password)
(if (= password 42)
x
(error "Go to jail without taking the $20,000")))))


(print-function incx) --> ?
(print-function resetx) --> ?
(print-function getx) --> ?


What we'd want is to print:

(let ((x 57)) ; the current value when we print it!
(defun incx (i) (incf x i))
(defun resetx () (setf x 0))
(defparameter getx (lambda (password)
(if (= password 42)
x
(error "Go to jail without taking the $20,000")))))

once for the three print-function calls.
What would you print if only one is called?
What would you print for:

(print-function incx)
(incx 34)
(print-function getx)

?


Have fun!


--
__Pascal Bourguignon__ http://www.informatimago.com/

There is no worse tyranny than to force a man to pay for what he does not
want merely because you think it would be good for him. -- Robert Heinlein
.



Relevant Pages

  • Re: Thread-safe closures
    ... LPP> the closure. ... The closure is called only once somewhere at the ... LPP> (defun teardown () ...
    (comp.lang.lisp)
  • Re: Is anything easier to do in java than in lisp?
    ... > fine as long as I don't care about bringing along their internal state. ... (defun save-function (fun lambda-form) ... (defmacro defun* (name &body body) ...
    (comp.lang.lisp)
  • Re: A "killer" macro
    ... (defun hello-world () ... On-exit is supposed to queue the formto run on scope exit; ... This feature is easily implemented with macros (note that defun is ... (defmacro in-scope (&body body) ...
    (comp.lang.lisp)
  • Re: Closures: Im not getting it
    ... Pascal Costanza wrote: ... > globally accessible via the name given in the DEFUN form. ... This is what makes a closure tick. ...
    (comp.lang.lisp)
  • Problem with Practical Common Lisp + sbcl
    ... (defun next-prime (number) ... ; in: DEFUN PRIMEP ... Does this not work in SBCL? ...
    (comp.lang.lisp)