Variable references in lisp
With lexical scope one does not have access to variables that are not in the current scope. You also cannot pass lexical variables to other functions directly. Lisp evaluates variables and passes the values bound to these variables. There is nothing like first-class references to variables.
Think functional!
(let ((a 1))
(values (lambda (new-value)
(setf a new-value))
(lambda () a)))
above returns two functions. One can read the variable, another one can write the variable.
Let's call the first function writer
and the second one reader
.
(defun increase-by-one (writer reader)
(funcall writer (1+ (funcall reader))))
So, to do what you want, the code needs a) be in the scope or b) have access to functions that are in the scope.
Also the variable could be global.
(defvar *counter* 1)
(defun increase-by-one (symbol)
(set symbol (1+ (symbol-value symbol))))
; note the use of SET to set a symbol value
(increase-by-one '*counter*)
This works for global variables that are represented by a symbol. It does not work for lexical variables - these are not represented by a symbol.
There is also a macro INCF
that increases a 'place' (for example a variable).
(incf a)
But a
is the variable in the current scope.
(defun foo (a)
(incf a)) ; increases the local variable a
The limit is seen here:
(defun foo (var)
(add-one-some-how var))
(let ((a 1))
(foo something-referencing-a))
There is no way to pass a direct reference of a
to FOO
.
The only way is to provide a function. We also have to rewrite FOO
, so that it calls the provided function.
(defun foo (f)
(funcall f 1)) ; calls the function with 1
(let ((a 1))
(foo (lambda (n)
(setf a (+ a n)))))
;; passes a function to foo that can set a
Of course, in Lisp you can make your own way make variable references, if you want to. The simplest approach is like this:
(defstruct reference getter setter)
(defmacro ref (place)
(let ((new-value (gensym)))
`(make-reference :getter (lambda () ,place)
:setter (lambda (,new-value)
(setf ,place ,new-value)))))
(defun dereference (reference)
(funcall (reference-getter reference)))
(defun (setf dereference) (new-value reference)
(funcall (reference-setter reference) new-value))
And then you can use it:
(defun increase-by-one (var-ref)
(incf (dereference var-ref)))
(defun test-inc-by-one (n)
(let ((m n))
(increase-by-one (ref m))
(values m n)))
(test-inc-by-one 10) => 11, 10