Address of a variable: Difference between revisions

→‎{{header|D}}: We can have references to arbitrary places as first-class values in Lisp if we want them badly enough.
m (simplified the Modula-2 code)
(→‎{{header|D}}: We can have references to arbitrary places as first-class values in Lisp if we want them badly enough.)
Line 154:
</lang>
 
=={{header|Common Lisp}}==
 
===Discussion===
 
Common Lisp has no means to take a simple reference to a ''place'' (the Lisp term for any one of many types of assignable storage locations). For instance, to access the slot of a structure, we need the structure itself (which is essentially a pointer) and a symbol denoting the slot name. To access an array, we need that array object, and an index. To access a local variable, we need the closure object, which is only accessible to code within the lexical scope, which is a long-winded way of saying, we can only access a variable via an expression which is in the lexical scope of the variable.
 
Yet, thanks to Lisp macros and lexical closures, we can create "locatives": values which behave like references to places.
 
===The Code===
 
The code below is found in Lisppaste (http://paste.lisp.org/display/71952)
 
<lang lisp>;;; References: pointer-like place locators that can be passed around.
;;; Kaz Kylheku <kkylheku@gmail.com>
;;; December 2008
 
;;; How to use:
;;;
;;; Produce a reference which "lifts" the place designated
;;; by form P:
;;;
;;; (ref p)
;;;
;;; Dereference a reference R to designate the original place:
;;;
;;; (deref r)
;;; (setf (deref r) 42) ;; store new value 42
;;;
;;; Shorthand notation instead of writing a lot of (deref)
;;; Over FORMS, A is a symbol macro which expands to
;;; (DEREF RA), B expands to (DEREF RB):
;;;
;;; (with-refs ((a ra) (b rb) ...)
;;;
;;; ... forms)
;;;
(defstruct ref
(get-func)
(set-func))
 
(defun deref (ref)
(funcall (ref-get-func ref)))
 
(defun (setf deref) (val ref)
(funcall (ref-set-func ref) val))
 
(defmacro ref (place-expression &environment env)
(multiple-value-bind (temp-syms val-forms
store-vars store-form access-form)
(get-setf-expansion place-expression env)
(when (cdr store-vars)
(error "REF: cannot take ref of multiple-value place"))
`(multiple-value-bind (,@temp-syms) (values ,@val-forms)
(make-ref
:get-func (lambda () ,access-form)
:set-func (lambda (,@store-vars) ,store-form)))))
 
(defmacro with-refs ((&rest ref-specs) &body forms)
`(symbol-macrolet
,(loop for (var ref) in ref-specs
collecting (list var `(deref ,ref)))
,@forms))</lang>
===A Few Notes===
It uses the standard Lisp function GET-SETF-EXPANSION (http://www.lispworks.com/documentation/HyperSpec/Body/f_get_se.htm) to obtain detailed information about any form designating a place, using which it is possible to generate code for writing to a place or accessing its value.
 
For instance, consider the place (cdr (aref x (incf i))): the CDR field of the cons cell which is pulled from array x at index i + 1. Note that accessing this place involves a side effect of incrementing i. So we can't simply capture this expression in a lexical closure and use that closure as a reference to the place. We would be invoking multiple side effects when working with that place. GET-SETF-EXPANSION will give us the pieces needed to compile this properly, so that (ref place) will evaluate the place expression just once, performing its side effects, if any, and (deref r) can be used on the resulting reference to access the place or store to it any number of times. A place like (cdr (aref x (incf i)) will be captured once as the address of the cons cell, so further uses of the place do not involve the array x, nor the variable i.
=={{header|D}}==
 
Anonymous user