Lisp HUG Maillist Archive

Using defparameter inside a function to make and bind a list of variables

This code is supposed to define symbols A B C, D, and E and assign values. However, the code does not work.

 

=========== Start of code =============

;;;; Using defparameter inside a function to make and

;;;; bind a list of variables

;;;;

;;;; define-variables is the function to call from the Listener.

;;;; define-variables-1 is the function under test

 

 

;;; recursively called function

(defun define-variables-1 (name-lst value-lst)

  (let ((cname (first name-lst))

        (cvalue (first value-lst)))

 

    (cond (cname

           (defparameter cname cvalue)

           (return-from define-variables-1

             (define-variables-1 (rest name-lst) (rest value-lst))))

 

 

          (t

           (return-from define-variables-1 nil)))))

         

 

;;; function that sets up the situation and calls

;;; the function under test

(defun define-variables ()

  (define-variables-1 (list 'a 'b 'c 'd 'e)

                      (list 1 2 3 'x 'y)))

 

 

 

=========== End of code =============

 

Running the code gives these warnings:

Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.

Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.

Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.

Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.

 

 

The code could work with SET instead of DEFPARAMETER. But really, DEFPARAMETER is just an example. I could be trying to use DEFCONSTANT or DEFVAR and the code would not work.

 

I could make the code work using EVAL, READ, and FORMAT to create the desired string to execute on each iteration.

 

Using SETF as an example of the kind of solution I seek:

 

You can do this in LispWorks, even though (if I understand) “defining variables using SETF” is undefined in the Common Lisp Standard:

 

=============== Begin session ===============

CL-USER 1 > (setf var 'x)

X

 

CL-USER 2 > (setf (symbol-value var) 'y)

Y

 

CL-USER 3 > x

Y

 

CL-USER 4 > var

X

=============== End session ===============

 

While symbol-value works for SETF (in LispWorks), symbol-value does not work for DEFPARAMETER. The error is that (SYMBOL-VALUE CNAME) is not a symbol. Of course it is not a symbol, it is a list. So I see that this list does not get evaluated to a symbol for DEFPARAMETER.

 

So, is there something I can do more along the lines of what makes SETF work? Can I do something other than use EVAL, READ, and FORMAT to make DEFPARAMETER work inside the function for the lists?

 

 

Ron Lewis

 

 

Re: Using defparameter inside a function to make and bind a list of variables

A simpe solution is to construct a defparamter function call, then evaluate it. 

(eval `(defparameter ,cname ,value))

Bruno

> On May 17, 2018, at 11:54, Ron Lewis <rlewis-4d@indinfer.com> wrote:
> 
> This code is supposed to define symbols A B C, D, and E and assign values. However, the code does not work.
>  
> =========== Start of code =============
> ;;;; Using defparameter inside a function to make and
> ;;;; bind a list of variables
> ;;;;
> ;;;; define-variables is the function to call from the Listener.
> ;;;; define-variables-1 is the function under test
>  
>  
> ;;; recursively called function
> (defun define-variables-1 (name-lst value-lst)
>   (let ((cname (first name-lst))
>         (cvalue (first value-lst)))
>  
>     (cond (cname
>            (defparameter cname cvalue)
>            (return-from define-variables-1
>              (define-variables-1 (rest name-lst) (rest value-lst))))
>  
>  
>           (t
>            (return-from define-variables-1 nil)))))
>           
>  
> ;;; function that sets up the situation and calls
> ;;; the function under test
> (defun define-variables ()
>   (define-variables-1 (list 'a 'b 'c 'd 'e)
>                       (list 1 2 3 'x 'y)))
>   
>  
>  
> =========== End of code =============
>  
> Running the code gives these warnings:
> Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.
> Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.
> Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.
> Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.
>  
>  
> The code could work with SET instead of DEFPARAMETER. But really, DEFPARAMETER is just an example. I could be trying to use DEFCONSTANT or DEFVAR and the code would not work.
>  
> I could make the code work using EVAL, READ, and FORMAT to create the desired string to execute on each iteration. 
>  
> Using SETF as an example of the kind of solution I seek:
>  
> You can do this in LispWorks, even though (if I understand) “defining variables using SETF” is undefined in the Common Lisp Standard:
>  
> =============== Begin session ===============
> CL-USER 1 > (setf var 'x)
> X
>  
> CL-USER 2 > (setf (symbol-value var) 'y)
> Y
>  
> CL-USER 3 > x
> Y
>  
> CL-USER 4 > var
> X
> =============== End session ===============
>  
> While symbol-value works for SETF (in LispWorks), symbol-value does not work for DEFPARAMETER. The error is that (SYMBOL-VALUE CNAME) is not a symbol. Of course it is not a symbol, it is a list. So I see that this list does not get evaluated to a symbol for DEFPARAMETER.
>  
> So, is there something I can do more along the lines of what makes SETF work? Can I do something other than use EVAL, READ, and FORMAT to make DEFPARAMETER work inside the function for the lists?
>  
>  
> Ron Lewis


_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html

Re: Using defparameter inside a function to make and bind a list of variables


> On 17 May 2018, at 17:54, Ron Lewis <rlewis-4d@indinfer.com> wrote:
> 
> This code is supposed to define symbols A B C, D, and E and assign values. However, the code does not work.
>  
> =========== Start of code =============
> ;;;; Using defparameter inside a function to make and
> ;;;; bind a list of variables
> ;;;;
> ;;;; define-variables is the function to call from the Listener.
> ;;;; define-variables-1 is the function under test
>  
>  
> ;;; recursively called function
> (defun define-variables-1 (name-lst value-lst)
>   (let ((cname (first name-lst))
>         (cvalue (first value-lst)))
>  
>     (cond (cname
>            (defparameter cname cvalue)
>            (return-from define-variables-1
>              (define-variables-1 (rest name-lst) (rest value-lst))))
>  
>  
>           (t
>            (return-from define-variables-1 nil)))))
>           
>  
> ;;; function that sets up the situation and calls
> ;;; the function under test
> (defun define-variables ()
>   (define-variables-1 (list 'a 'b 'c 'd 'e)
>                       (list 1 2 3 'x 'y)))
>   
>  
>  
> =========== End of code =============
>  
> Running the code gives these warnings:
> Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.
> Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.
> Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.
> Warning: (DEFVAR CNAME) defined more than once in D:\Users\Ron\Documents\LispWorks\AEM\try.lisp.
>  
>  
> The code could work with SET instead of DEFPARAMETER. But really, DEFPARAMETER is just an example. I could be trying to use DEFCONSTANT or DEFVAR and the code would not work.
>  
> I could make the code work using EVAL, READ, and FORMAT to create the desired string to execute on each iteration. 
>  
> Using SETF as an example of the kind of solution I seek:
>  
> You can do this in LispWorks, even though (if I understand) “defining variables using SETF” is undefined in the Common Lisp Standard:
>  
> =============== Begin session ===============
> CL-USER 1 > (setf var 'x)
> X
>  
> CL-USER 2 > (setf (symbol-value var) 'y)
> Y
>  
> CL-USER 3 > x
> Y
>  
> CL-USER 4 > var
> X
> =============== End session ===============
>  
> While symbol-value works for SETF (in LispWorks), symbol-value does not work for DEFPARAMETER. The error is that (SYMBOL-VALUE CNAME) is not a symbol. Of course it is not a symbol, it is a list. So I see that this list does not get evaluated to a symbol for DEFPARAMETER.
>  
> So, is there something I can do more along the lines of what makes SETF work? Can I do something other than use EVAL, READ, and FORMAT to make DEFPARAMETER work inside the function for the lists?


Your function define-variables-1 cannot work because defparameter is a macro. (you may use eval to evaluate a macro-form that you would build at run-time, as indicated by Bruno.

Reading the specification of defparameter, you could understand what it does, and when it does it.
You could then implement a function doing the same thing, at run-time (when the function is called).

But in the case of defparameter it would be mostly futile, because the rest of your program that has been compiled at compilation-time couldn’t possibly use variables that you define at run-time (unless you have a time-machine, in which case, please send a copy this way).


Here is an implementation of the defparameter macro.  Notice the %note-dynamic-variable function that needs to be implementation dependant, to provide with the compiler the information that there is a new dynamic variable, and therefore that it should not signal error for a free variable when compiling the rest of the program.

Follows make-parameter, a function that does the same thing, but at run-time!  Using it is futile.


----(/tmp/dp.lisp)———————————————————————————

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun %note-dynamic-variable (name)
    (declare (ignorable name))
   #| do something to tell the compiler that the symbol named NAME denotes a dynamic variable. |#
   (values)))

(defmacro .defparameter (name initial-value &optional docstring)
  `(progn
     (eval-when (:compile-toplevel)
       (%note-dynamic-variable ',name))
     (declaim (special ,name))
     (setf (symbol-value ',name) ,initial-value
           (documentation ',name 'variable) ,@docstring)
     ',name))

(defun make-parameter (name initial-value &optional docstring)
  (%note-dynamic-variable name) ; oops! Too late!
  (proclaim `(special ,name))
  (setf (symbol-value name) initial-value
        (documentation name 'variable) docstring))

(defun foo ()
  (make-parameter '*foo* (+ 2 40) "The exponent of the mass of the Milky-Way in kilogram.")
  (expt 10 *foo*))

(defun bar ()
  (let ((*foo* 10))
    (baz)))

(defun baz ()
  (expt 10 *foo*))

(defun quux ()
  (list (foo)
        (bar)))

————————————————————————————————————

As you can see, when you compile this file, with the function foo making a parameter *foo*, the compiler signals a warning about the variabls *foo* that are not defined, and the lexical variable *foo* that is not used. 


cl-user> (compile-file "/tmp/dp.lisp")
;Compiler warnings for "/tmp/dp.lisp" :
;   In foo: Undeclared free variable *foo*
;   In bar: Unused lexical variable *foo*
;   In baz: Undeclared free variable *foo*
#P"/private/tmp/dp.dx64fsl"
t
t
cl-user> (load #P"/private/tmp/dp.dx64fsl")
#P"/private/tmp/dp.dx64fsl"
cl-user> (quux)
(1000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000)


And it doesn’t work as it should, since the binding of *foo* to 10 has been compiled as a lexical binding, so when baz is called, it cannot access that value, but it takes the symbol-value which is 42, just like foo.




-- 
__Pascal J. Bourguignon__





_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html

Re: Using defparameter inside a function to make and bind a list of variables

I may be missing something here but if you want to give a bunch of symbols top-level values and declare them globally special then something like this would work, I think:

(defun define-globals (vars vals)
  (mapc (lambda (var val)
            (proclaim `(special ,var))
            (setf (symbol-value var) val))
          vars vals))

A nicer (I think) and much more comprehensive version of this, with an argument convention I prefer, is

(defun define-globals (bindings)
  (flet ((define-one-global (var &optional
                              (val nil valp)
                              (doc nil docp))
           (proclaim `(special ,var))
           (when valp
             (setf (symbol-value var) val))
           (when docp
             (setf (documentation var 'variable) doc))
           var))
    (loop for b in bindings
          collect (etypecase b
                    (symbol
                     (define-one-global b))
                    (cons
                     (ecase (length b)
                       (2
                        (destructuring-bind (var val) b
                          (assert (typep var 'symbol))
                          (define-one-global var val)))
                        (3
                         (destructuring-bind (var val doc) b
                           (assert (and (typep var 'symbol)
                                        (typep doc 'string)))
                           (define-one-global var val doc)))))))))

Neither of these make any attempt to mimic defparameter but what they do is reasonably clear I think, and portable (also, I think).

Both of these have the obvious problem of compile-time / run-time: a file with content like

(define-globals '((*a* 1)))

(defun foo ()
  *a*)

Is going to have compile-time warnings and run-time errors.  But I think that

(eval-when (:load-toplevel :compile-toplevel :execute)
  (define-globals '((*a* 1))))

(defun foo ()
  *a*)

Will be OK, or alternatively placing the define-globals form into a file which is compiled and loaded before any functions which rely on the globals are compiled or run.

(define-globals is a horrid name for a function though, since it smells like a macro).

_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html

Re: Using defparameter inside a function to make and bind a list of variables



Le 18 May 2018 à 18:44, Tim Bradshaw <tfb@tfeb.org> a écrit :

I may be missing something here but if you want to give a bunch of symbols top-level values and declare them globally special then something like this would work, I think:

(defun define-globals (vars vals)
 (mapc (lambda (var val)
           (proclaim `(special ,var))
           (setf (symbol-value var) val))
         vars vals))

A nicer (I think) and much more comprehensive version of this, with an argument convention I prefer, is

(defun define-globals (bindings)
 (flet ((define-one-global (var &optional
                             (val nil valp)
                             (doc nil docp))
          (proclaim `(special ,var))
          (when valp
            (setf (symbol-value var) val))
          (when docp
            (setf (documentation var 'variable) doc))
          var))
   (loop for b in bindings
         collect (etypecase b
                   (symbol
                    (define-one-global b))
                   (cons
                    (ecase (length b)
                      (2
                       (destructuring-bind (var val) b
                         (assert (typep var 'symbol))
                         (define-one-global var val)))
                       (3
                        (destructuring-bind (var val doc) b
                          (assert (and (typep var 'symbol)
                                       (typep doc 'string)))
                          (define-one-global var val doc)))))))))

Neither of these make any attempt to mimic defparameter but what they do is reasonably clear I think, and portable (also, I think).

Both of these have the obvious problem of compile-time / run-time: a file with content like

(define-globals '((*a* 1)))

(defun foo ()
 *a*)

Is going to have compile-time warnings and run-time errors.  

Yes.  And the question is why you want to add global variables at run-time.


But I think that

(eval-when (:load-toplevel :compile-toplevel :execute)
 (define-globals '((*a* 1))))

(defun foo ()
 *a*)

Will be OK, or alternatively placing the define-globals form into a file which is compiled and loaded before any functions which rely on the globals are compiled or run.

Once you start using eval-when to do things at compilation time, you should consider writing a macro instead!

We still don’t know what you want to do? Why do you want to create global variables at run-time, that cannot possibly be referenced (for lack of a time machine) by the code that is already compiled?


(define-globals is a horrid name for a function though, since it smells like a macro).

Yes. Usually those functions are named MAKE-something. (eg. defpackage -> make-package)
Note that once you have functions, you can use map or other iteration operators, so in general, it’s not required to define functions processing multiple elements.

You could name it make-global-variable, and use it like this:
  (mapc (function make-global-variable) ‘(*a* *b* *c*) ‘(1 2 3))
instead of:
  (make-global-variables ‘(*a* *b* *c*) ‘(1 2 3))

Your loop body can be written more concisely as:

(if (symbolp binding)
    (make-global-variable binding nil nil)
    (destructuring-bind (var &optional val (doc nil docp)) binding
      (check-type var symbol)
      (when docp (check-type doc string))
      (make-global-variable var val doc)))


-- 
__Pascal J. Bourguignon__




Re: Using defparameter inside a function to make and bind a list of variables

On 18 May 2018, at 18:55, Pascal Bourguignon <pjb@informatimago.com> wrote:

Once you start using eval-when to do things at compilation time, you should consider writing a macro instead!

yes, and in fact if I was doing this I'd do just that -- have a make-* function and a define-* macro which expanded into a suitable (eval-when (...) (make-* ....))

We still don’t know what you want to do? Why do you want to create global variables at run-time, that cannot possibly be referenced (for lack of a time machine) by the code that is already compiled?

Just to be clear I'm not the original referent of 'you' here.  It is the sort of thing people used to do a lot of but I'd certainly find other ways of doing the same thing I think  -- if I needed some kind of dynamically-defined dynamically-scoped object I'd write some syntax for that and not use specials (or, probably I would use some internal special in the implementation but not at the user level).

--tim
Updated at: 2020-12-10 08:30 UTC