Created
February 22, 2012 15:40
-
-
Save manuel/1885594 to your computer and use it in GitHub Desktop.
Adventures in Hygienic Fexprs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; As part of the implementation of Virtua's [1] object system I need | |
;; a facility called DEFINE-CONSTRUCTOR that defines a convenient | |
;; function for creating an instance of a class and setting its slots. | |
(define-constructor make-person Person (firstname lastname)) | |
;; should be operationally equivalent to | |
(define make-person | |
(lambda (firstname lastname) | |
(let ((obj (make-instance Person))) | |
(set-slot! obj "firstname" firstname) | |
(set-slot! obj "firstname" lastname) | |
obj))) | |
;; Bad hygiene already bites! What if one of the slots is called | |
;; OBJ? In an unhygienic macro system, that code would already | |
;; break. | |
;; | |
;; One of the promises of Kernel's [2] fexprs is that they obviate the | |
;; need for a hygienic macro system [3]. | |
;; | |
;; What I came up with was this somewhat shoddy code: | |
(define define-constructor | |
(vau (name class slots) env | |
(eval (list define name | |
(list lambda slots | |
(list construct-with-slots class slots))) | |
env))) | |
;; This part is easy. It lets... | |
(define-constructor make-person Person (firstname lastname)) | |
;; ...have the same effect as this: | |
(define make-person | |
(lambda (firstname lastname) | |
(construct-with-slots Person (firstname lastname)))) | |
;; As usual, the funny stuff happens in the helper function: | |
;; CONSTRUCT-WITH-SLOTS receives the name of the class and the list of | |
;; slot names. (It's important that it appears in a LAMBDA, that is | |
;; environment, that does bind these slot names to their values. | |
;; Quite ugly, but the best I could do.) | |
;; | |
;; First it creates the instance of the class, which it obtains by | |
;; evaluating the name of the class in its dynamic environment. | |
;; (Which is probably a bug -- shouldn't it be the static environment? | |
;; I need to think about this.) | |
;; | |
;; Then it loops over the slot names and sets each to its value, which | |
;; it obtains by evaluating the name in the dynamic environment -- that | |
;; is, of the LAMBDA that contains it. | |
(define construct-with-slots | |
(vau (class slots) env | |
(let ((obj (make-instance (eval class env)))) | |
(map-list (lambda (slot) | |
(set-slot! obj (to-string slot) (eval slot env))) | |
slots) | |
obj))) | |
;; This code is immune to the problem of one of the slot names | |
;; clashing with the names of variables we use internally in the | |
;; implementation. IOW, even if one the slot names is OBJ, that name | |
;; will not clash with our local OBJ variable - because we look up | |
;; the slot names in the enclosing environment. | |
;; | |
;; Fexprs make the implementation of a Lisp with hygiene *radically* | |
;; simpler. I have yet to see whether this benefit to implementors | |
;; doesn't increase the burden on programmers. | |
;; | |
;; -- Manuel Simoni (msimoni@gmail.com), 2012 Feb 22 | |
;; | |
;; [1] https://github.com/manuel/virtua | |
;; [2] http://web.cs.wpi.edu/~jshutt/kernel.html | |
;; [3] http://lambda-the-ultimate.org/node/4345#comment-66868 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment