Skip to content

Instantly share code, notes, and snippets.

@manuel
Created February 22, 2012 15:40
Show Gist options
  • Save manuel/1885594 to your computer and use it in GitHub Desktop.
Save manuel/1885594 to your computer and use it in GitHub Desktop.
Adventures in Hygienic Fexprs
;; 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