From posting-system@google.com Tue Feb 26 17:01:34 2002 Date: Tue, 26 Feb 2002 15:01:31 -0800 From: oleg-at-pobox.com (oleg-at-pobox.com) Newsgroups: comp.lang.scheme Subject: C pointer <-> Scheme closure: how to emulate & in Scheme Message-ID: <7eb8ac3e.0202261501.652167e2@posting.google.com> Status: OR There is a deep connection between C pointers on one hand, and Scheme closures that respond to the messages ref and set on the other hand. That means we can precisely emulate the semantics of '&' in Scheme. OTH, we can say that C has a limited form of closures. This article demonstrates the connection by re-writing -- almost verbatim -- two C code fragments into Scheme. The C samples rely on pointers to let invoked functions share and mutate the variables local to the caller. The technique can be easily generalized to permit pointer arithmetics as well. C code that requires the use of pointers: function swap: void swap(int * px, int * py) { int temp = * px; *px = * py; *py = temp; } int main(void) { int x = 1, y = 2; printf("Before: x=%d y=%d\n",x,y); swap(&x,&y); printf("After: x=%d y=%d\n",x,y); } A pointer can be dereferenced to read the pointed value, or to mutate the pointed value. In C terms, a pointer can be dereferenced to an R-value or to an L-value. In the Scheme code below, we distinguish the two uses more explicitly. In both cases the difference is syntactically apparent anyway. The above fragment in Scheme: (define (swap px py) (let ((temp (* px))) ((*= px) (* py)) ((*= py) temp))) (define (test1) (let ((x 1) (y 2)) (for-each display (list "Before: x= " x " and y= " y #\newline)) (swap (& x) (& y)) (for-each display (list "After: x= " x " and y= " y #\newline)))) (test1) The definitions for '&' and for the re-defined '*' are given in the appendix. Another example, this time with pointers to pointers: void modify(int ** p, int * py) { **p = *py * *py; *p = py; } int main(void) { int x = 1, y = 2; int * px = &x; int ** pp = & px; printf("Before: x=%d y=%d **p=%d\n",x,y,**pp); modify(pp,&y); printf("After: x=%d y=%d **p=%d\n",x,y,**pp); } The same thing in Scheme: (define (modify p py) ((*= (* p)) (* (* py) (* py))) ((*= p) py) ) (define (test2) (let* ((x 1) (y 2) (px (& x)) (pp (& px))) (for-each display (list "Before: x= " x " and y= " y " and **p=" (* (* pp)) #\newline)) (modify pp (& y)) (for-each display (list "After: x= " x " and y= " y " and **p=" (* (* pp)) #\newline)) )) (test2) It looks the same and really works the same. Appendix: ; Making a pointer -- a corresponding Scheme closure, to be precise. ; The original paper had the following low-level macro ;(define-macro (& x) ; `(lambda (action) ; (case action ; ((ref) ,x) ; ((set) (lambda (new-val) (set! ,x new-val)))))) ; The same macro as a syntax rule (define-syntax & (syntax-rules () ((& x) (lambda (action) (case action ((ref) x) ((set) (lambda (new-val) (set! x new-val)))))))) ; Dereferencing the pointer to an R-value (define (p-ref ptr) (ptr 'ref)) ; Dereferencing the pointer to an L-value and updating the pointed ; variable (define (*= ptr) (ptr 'set)) ; Overloading the * operation with C-style dereferencing ; so we could write (* p) rather than (p-ref p) (let ((old-* *)) (set! * (lambda args (if (and (pair? args) (null? (cdr args)) (procedure? (car args))) (p-ref (car args)) (apply old-* args)))))