Fred_2
Fred_2

Reputation: 249

LISP parameter binding

I have to define a LISP function that assigns to a parameter the result of another function, like this:

(defun assign-param (x)
  (defparameter param (generic-funct x)))

the result of assign-param is PARAM, at the first call. If i do another call, the function assign-param returns again PARAM, but this way:

Warning: (DEFVAR PARAM) defined more than once in 
C:\Users\****\Desktop\****\****.lisp.
PARAM

I tried using defvar instead of defparameter with the same result.

Is there a way to avoid this warning message?

Is there a way to auto-generate other parameters if one already exists? (ex: if param is already in memory, assign to param1, param2...)

Upvotes: 2

Views: 630

Answers (2)

Kaz
Kaz

Reputation: 58578

Try:

(defvar *param*) ;; at the top level: define the variable

;; Now actually just a pure assignment in the function now
(defun assign-param (x)
  (setf *param* (generic-funct x)))

I added the * "earmuffs", because I can't in good conscience introduce a dynamic variable without; my good name is attached to this answer.

Really, why though? Why hide an assignment side effect behind a function. Its almost certainly better to document the *param* variable and let the caller do the assignment itself.

Wrapping a Lisp dynamic variable behind this sort of access wall only cripples it.

Lisp lets us do this:

;; locally re-bind variable
(let ((*param* (some-value)))
  (code)
  ...))

the (code) body will execute with *param* having the value from (some-value). When the let terminates, the previous value is restored, no matter how it terminates. The variable is still global: the altered value is seen inside functions called out of the body, not just by forms that are lexically scoped in the let, as with a lexical variable.

(The reason *param* is treated this way is that the defvar and defparameter forms specially mark the symbol for this type of binding and scoping. It becomes a "special variable". And that is why we use *...* earmuffs: to create a separate namespace which loudly identifies these non-lexically-scoped variables, preventing us from accidentally converting lexical variables in our code into special variables.)

If we hide the existence of *param*, behind a function, we lose this flexibility; we have reduced it to a dumb global that can only be clobbered with calls to assign-param.

The assign-param function says to me (even after being fixed to use setf with defvar on the outside), "I don't understand Lisp and I'm scared of global variables from my experience with other languages".

Upvotes: 2

sds
sds

Reputation: 60014

You are doing something terribly wrong.

defvar and defparameter are "top-level" forms.

There is no reason to use them inside a function.

What you might want to do is something like

(defvar *param*)
(defun assign-param (x)
  (setq *param* (generic-funct x))
  '*param*) ; so that `assign-param` returns the symbol instead of the new value, like `defvar`

However, your last question about param1, param2 &c makes little sense (yes, it is possible to do it, and, no, you do not want to do it).

What you might want to be doing

You appear to want to have a set of global parameters, set dynamically from, say, environment or a config file.

There are two main way to do this: hash tables and packages.

Hash Tables

Simple and straighforward; associate any value with any string:

(defvar *parameters* (make-hash-table :test 'equal))
(defun param-value (param-name)
  (gethash param-name *parameters*))
(defun (setf param-value) (new-value param-name)
  (setf (gethash param-name *parameters*) new-value))
(param-value "foo")
==> NIL; NIL
(setf (param-value "foo") "bar")
==> "bar"
(param-value "foo")
==> "bar"; T
(defun delete-param (param-name)
  (remhash param-name *parameters*))

Note that you should modify param-value to signal an error if the second value of gethash is nil, i.e., the parameter does not exist.

Packages

More involved, but you can associate more "stuff" with your parameter objects:

(defvar *parameters* (make-package "parameters" :use ()))
(defun param-value (param-name)
  (multiple-value-bind (param status) (find-symbol param-name *parameters*)
    (unless status
      (error "Parameter %s does not exist" param-name))
    (symbol-value param)))
(defun (setf param-value) (new-value param-name)
  (setf (symbol-value (intern param-name *parameters*)) new-value))
(defun delete-param (param-name)
  (multiple-value-bind (param status) (find-symbol param-name *parameters*)
    (when status
      (unintern param))))

The major advantage of this approach is the integration of read with the packages which would make config file parsing easier.

A minor advantage is that you can store not just a value, but also a function and a property list in your parameter.

Upvotes: 6

Related Questions