From: Tamas Papp
Subject: saving values for reloading and macro de-uglification
Date: 
Message-ID: <87sl5et7h2.fsf@pu100877.student.princeton.edu>
Suppose that I have performed a lengthy number-crunching calculation,
stored the result in *foo*, and want to save it by creating a Lisp
source file that when loaded will define *foo* with its current value.
(I know I could save an image, but I might not need all that is in
there, just a few variables).

I came up with this macro:

(defmacro save-values (filename &rest value-forms)
  "Create a Lisp source file at filename that, when loaded, defines
parameters with the given names to hold values at the time the macro
is executed.  Also accept (name . value) pairs, saving the value of
the variable value to be loaded into name."
  `(with-open-file (stream ,filename :direction :output :if-exists :supersede
			   :if-does-not-exist :create)
     ,@(iter
	(for v :in value-forms)
	(multiple-value-bind (name value)
	    (cond
	      ((atom v) 
	       (assert (symbolp v))
	       (values v v))
	      (t (let ((name (car v))
		       (value (cdr v)))
		   (assert (and (symbolp name) (symbolp value)))
		   (values name value))))
	  (collecting `(progn
			 (format stream "(defparameter ")
			 (prin1 ',name stream)
			 (format stream " ")
			 (prin1 ,value stream)
			 (format stream ")~%")))))))

Example:

(defparameter *b* 2)
(defparameter *c* #(1 2 3))
(defparameter *d* (make-array 3 :element-type 'fixnum :initial-contents '(1 2 3)))

creates

(defparameter *B* 2)
(defparameter *HOLDS-C* #(1 2 3))
(defparameter *D* #(1 2 3))

in /tmp/foo.lisp.

A couple of questions:

1. is this the way to do what I am trying to accomplish?  looks
contrived.

2. Is it possible to de-uglify the end of that macro?  Assembling
stuff from strings looks so un-lispy.

3. *c* and *d* above have the same printed representation, but they
are different things, eg with different array-element-type.  Is it
possible to print a representation which would reflect this when read?

Note that the values I am trying to save this way are vectors, arrays
and possibly some structures.  Nothing fancy (eg circular lists,
objects referencing each other etc).

Thanks,

Tamas

From: Willem Broekema
Subject: Re: saving values for reloading and macro de-uglification
Date: 
Message-ID: <1190005952.736112.65400@o80g2000hse.googlegroups.com>
On Sep 17, 3:18 am, Tamas Papp <······@gmail.com> wrote:
> in /tmp/foo.lisp.
>
> Example:
>
> (defparameter *b* 2)
> (defparameter *c* #(1 2 3))
> (defparameter *d* (make-array 3 :element-type 'fixnum :initial-contents '(1 2 3)))
>
> creates
>
> (defparameter *B* 2)
> (defparameter *HOLDS-C* #(1 2 3))
> (defparameter *D* #(1 2 3))
> A couple of questions:
>
> 1. is this the way to do what I am trying to accomplish?  looks
> contrived.

As an alternative to directly writing objects to fasl files, you could
define a custom defvar-like construct that moves the complex
calculation to compile time. Then it is the task of the compiler to
write that object to the fasl file:

(defmacro def-complex-var (var val &optional (doc nil doc-p))
  (let ((val-now (eval val)))
    `(defvar ,var ',val-now ,@(when doc-p (list doc)))))

(I'm not sure the above (eval ..) is correct style. Is this a
situation where eval-when should be used?)

The compilation of a file with the next forms triggers the complex
calculation:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun complex-calculation ()
    (warn "Doing complex calculation")
    (make-array 3 :element-type 'fixnum :initial-contents (list 1 2
3))))

(defun simple-calculation ()
  (warn "Doing simple calculation.")
  2)

(def-complex-var *a* (complex-calculation))
(defvar *b* (simple-calculation))

cl-user(2): :cf tmp
;;; Compiling file tmp.cl
Warning: Doing complex calculation
;;; Writing fasl file tmp.fasl
;;; Fasl write complete

cl-user(3): :ld tmp
; Fast loading /Users/willem/dev/lisp/python/tmp.fasl
Warning: Doing simple calculation.
cl-user(4):


> 2. Is it possible to de-uglify the end of that macro?  Assembling
> stuff from strings looks so un-lispy.

(format stream "~A" `(defparameter ,name ',value))

It is safer to quote the value in the expansion, in case it is a list.

> 3. *c* and *d* above have the same printed representation, but they
> are different things, eg with different array-element-type.  Is it
> possible to print a representation which would reflect this when read?

Not sure you still need this, but that should be possible with a
custom pretty printer dispatch table combined with corresponding
reader macros.

> Note that the values I am trying to save this way are vectors, arrays
> and possibly some structures.  Nothing fancy (eg circular lists,
> objects referencing each other etc).

Even if you had shared structure the above approach should work fine.
Manually writing objects to fasl files should also be okay, provided
the writing happens in one fasl write call. (Allegro's excl:fasl-write
takes a fasl-circle argument.)

- Willem
From: Carl Taylor
Subject: Re: saving values for reloading and macro de-uglification
Date: 
Message-ID: <xYkHi.556960$p47.99205@bgtnsc04-news.ops.worldnet.att.net>
Tamas Papp wrote:

> Suppose that I have performed a lengthy number-crunching calculation,
> stored the result in *foo*, and want to save it by creating a Lisp
> source file that when loaded will define *foo* with its current value.

[...]

> Note that the values I am trying to save this way are vectors, arrays
> and possibly some structures.  Nothing fancy (eg circular lists,
> objects referencing each other etc).

To do this kind of thing, including the saving of global hash tables, I like to 
use the LispWorks operators to dump the desired objects to a fasl file, and then 
execute the corresponding system load function.  Often a good place to reload 
the saved objects is under an (eval-when (:load-toplevel) ....).

No doubt other CL implementations have similar system functions.  Of course this 
method is useless if portability is important.

The LW operators I'm talking about are: <with-output-to-fasl-file>, <dump-form>, 
and <sys:load-data-file>.  I have a little toy program with a hash table I'll 
post if you want to see it.

Carl Taylor 
From: ··········@aol.com
Subject: Re: saving values for reloading and macro de-uglification
Date: 
Message-ID: <1190006212.440651.146510@22g2000hsm.googlegroups.com>
On Sep 16, 9:18 pm, Tamas Papp <······@gmail.com> wrote:
> Suppose that I have performed a lengthy number-crunching calculation,
> stored the result in *foo*, and want to save it by creating a Lisp
> source file that when loaded will define *foo* with its current value.
> (I know I could save an image, but I might not need all that is in
> there, just a few variables).
>
> I came up with this macro:

OK, I think you should be banned from writing macros for a while. Why
is this a macro?  Am I missing something?

Anyway, I'd like to suggest that you check out the hyperspec entries
for "write" and "read". I'd also like to suggest that if your code
explicitly writes the string "defparameter" to a file you have
probably gone very far astray (that or you are a fricking genius).

There are a lot of reasonable ways to save state. This isn't one of
them.
From: Tamas Papp
Subject: Re: saving values for reloading and macro de-uglification
Date: 
Message-ID: <87odg1ts5d.fsf@pu100877.student.princeton.edu>
··········@aol.com writes:

> On Sep 16, 9:18 pm, Tamas Papp <······@gmail.com> wrote:
>> Suppose that I have performed a lengthy number-crunching calculation,
>> stored the result in *foo*, and want to save it by creating a Lisp
>> source file that when loaded will define *foo* with its current value.
>> (I know I could save an image, but I might not need all that is in
>> there, just a few variables).
>>
>> I came up with this macro:
>
> OK, I think you should be banned from writing macros for a while. Why
> is this a macro?  Am I missing something?

You are right, this shouldn't be a macro.

> Anyway, I'd like to suggest that you check out the hyperspec entries
> for "write" and "read". I'd also like to suggest that if your code
> explicitly writes the string "defparameter" to a file you have
> probably gone very far astray (that or you are a fricking genius).

Probably the first explanation is the right one ;-)

> There are a lot of reasonable ways to save state. This isn't one of
> them.

Can you please elaborate?  I would like to learn how to do it right.
save-object posted by Rainer is neat, but if you say there are a lot
of ways, other ones would be interesting.

Tamas
From: Rainer Joswig
Subject: Re: saving values for reloading and macro de-uglification
Date: 
Message-ID: <joswig-51E4FF.10505917092007@news-europe.giganews.com>
In article <··············@pu100877.student.princeton.edu>,
 Tamas Papp <······@gmail.com> wrote:

> Suppose that I have performed a lengthy number-crunching calculation,
> stored the result in *foo*, and want to save it by creating a Lisp
> source file that when loaded will define *foo* with its current value.
> (I know I could save an image, but I might not need all that is in
> there, just a few variables).
> 
> I came up with this macro:
> 
> (defmacro save-values (filename &rest value-forms)
>   "Create a Lisp source file at filename that, when loaded, defines
> parameters with the given names to hold values at the time the macro
> is executed.  Also accept (name . value) pairs, saving the value of
> the variable value to be loaded into name."
>   `(with-open-file (stream ,filename :direction :output :if-exists :supersede
> 			   :if-does-not-exist :create)
>      ,@(iter
> 	(for v :in value-forms)
> 	(multiple-value-bind (name value)
> 	    (cond
> 	      ((atom v) 
> 	       (assert (symbolp v))
> 	       (values v v))
> 	      (t (let ((name (car v))
> 		       (value (cdr v)))
> 		   (assert (and (symbolp name) (symbolp value)))
> 		   (values name value))))
> 	  (collecting `(progn
> 			 (format stream "(defparameter ")
> 			 (prin1 ',name stream)
> 			 (format stream " ")
> 			 (prin1 ,value stream)
> 			 (format stream ")~%")))))))

Macro alert. Macro alert. Macro alert.

> 
> Example:
> 
> (defparameter *b* 2)
> (defparameter *c* #(1 2 3))
> (defparameter *d* (make-array 3 :element-type 'fixnum :initial-contents '(1 2 3)))
> 
> creates
> 
> (defparameter *B* 2)
> (defparameter *HOLDS-C* #(1 2 3))
> (defparameter *D* #(1 2 3))
> 
> in /tmp/foo.lisp.
> 
> A couple of questions:
> 
> 1. is this the way to do what I am trying to accomplish?  looks
> contrived.
> 
> 2. Is it possible to de-uglify the end of that macro?  Assembling
> stuff from strings looks so un-lispy.
> 
> 3. *c* and *d* above have the same printed representation, but they
> are different things, eg with different array-element-type.  Is it
> possible to print a representation which would reflect this when read?
> 
> Note that the values I am trying to save this way are vectors, arrays
> and possibly some structures.  Nothing fancy (eg circular lists,
> objects referencing each other etc).
> 
> Thanks,
> 
> Tamas

In older times people used "save-object.lisp" .

It might need to clean-up (but don't remove the lispm code, yet ;-) ).

http://www.n-a-n-o.com/lisp/save-object-10.2.lisp

-- 
http://lispm.dyndns.org
From: Giorgos Keramidas
Subject: Re: saving values for reloading and macro de-uglification
Date: 
Message-ID: <874phmkfd9.fsf@kobe.laptop>
On Sun, 16 Sep 2007 21:18:33 -0400, Tamas Papp <······@gmail.com> wrote:
> Suppose that I have performed a lengthy number-crunching calculation,
> stored the result in *foo*, and want to save it by creating a Lisp
> source file that when loaded will define *foo* with its current value.
> (I know I could save an image, but I might not need all that is in
> there, just a few variables).
>
> I came up with this macro:
>
> (defmacro save-values (filename &rest value-forms)
>   "Create a Lisp source file at filename that, when loaded, defines
> parameters with the given names to hold values at the time the macro
> is executed.  Also accept (name . value) pairs, saving the value of
> the variable value to be loaded into name."
>   `(with-open-file (stream ,filename :direction :output :if-exists :supersede
> 			   :if-does-not-exist :create)
>      ,@(iter
> 	(for v :in value-forms)
> 	(multiple-value-bind (name value)
> 	    (cond
> 	      ((atom v)
> 	       (assert (symbolp v))
> 	       (values v v))
> 	      (t (let ((name (car v))
> 		       (value (cdr v)))
> 		   (assert (and (symbolp name) (symbolp value)))
> 		   (values name value))))
> 	  (collecting `(progn
> 			 (format stream "(defparameter ")
> 			 (prin1 ',name stream)
> 			 (format stream " ")
> 			 (prin1 ,value stream)
> 			 (format stream ")~%")))))))
>
> Example:
>
> (defparameter *b* 2)
> (defparameter *c* #(1 2 3))
> (defparameter *d* (make-array 3 :element-type 'fixnum :initial-contents '(1 2 3)))
>
> creates
>
> (defparameter *B* 2)
> (defparameter *HOLDS-C* #(1 2 3))
> (defparameter *D* #(1 2 3))
>
> in /tmp/foo.lisp.

Hmmm, why *HOLDS-C* and not just *C*?

> A couple of questions:
>
> 1. is this the way to do what I am trying to accomplish?  looks
> contrived.
>
> 2. Is it possible to de-uglify the end of that macro?  Assembling
> stuff from strings looks so un-lispy.

Yes.  Use a printer macro:

  CL-USER> (defparameter *foo* '(1 2 3))
  *FOO*
  CL-USER> (defmacro defparameter-of (foo)
             `(format nil "(DEFPARAMETER ~A ~A)"
                      (symbol-name (quote ,foo)) ,foo))
  DEFPARAMETER-OF
  CL-USER> (macroexpand-1 '(defparameter-of *foo*))
  (FORMAT NIL "(DEFPARAMETER ~A ~A)" (SYMBOL-NAME '*FOO*) *FOO*)
  T
  CL-USER> (defparameter-of *foo*)
  "(DEFPARAMETER *FOO* (1 2 3))"
  CL-USER>

The macro is simple enough that you can copy/paste it directly at the
place where it is used.  I just wrote it as a DEFMACRO to give it a nice
name for the testing session above.

> 3. *c* and *d* above have the same printed representation, but they
> are different things, eg with different array-element-type.  Is it
> possible to print a representation which would reflect this when read?

Not sure.

You are not only going to lose information of this sort, however.

Circular structures, shared cons cells, and other things which are
preserved in a Lisp image, will be gone after you "dump" like this and
reload by reading the file.