4 ms·
Have you thought about handling this at the language level? Instead of allocating the closure on the server you send a string representation of the closure to t
by jules 18y ago
Have you thought about handling this at the language level? Instead of allocating the closure on the server you send a string representation of the closure to the client.
First make sure that the lambda doesn't have free variables by adding the free variables to the argument list. (lambda (x) (+ n x)) => (lambda (x n) (+ n x)) Because the resulting lambda doesn't have any free variables you can turn it into a global function. (lambda (x n) (+ n x)) => function#345 and somewhere else you (define (function#345 x n) (+ n x)).
Next you store the values of the free variables in a hash table (use the memory location of the data as the key for the hash). Now you have a string representation for this closure. The representation contains the function's name (function#345) and the keys of the free variables in the hash table.
An example. Assume that users are represented by a cons cell (cons user-name karma). We're writing a page that has a link called "increment karma". When you click this link your karma is incremented.
(let ((user the-current-user))
(display-link "increment karma"
(lambda () (set-cdr user (+ 1 (cdr user))))))
First transform this to:
(define (function#345 user)
(set-cdr user (+ 1 (cdr user))))
(let ((user the-current-user))
(display-link "increment karma"
(make-closure function#345 user)))
make-closure in pseudo code (assuming there is one free variable/argument):
(define (make-closure fn arg)
(add-to-hash *free-variables* (memory-location arg) arg)
(add-to-hash *functions* (memory-location fn) fn)
(concat (to-string (memory-location fn)) "/" (to-string (memory-location arg))))
When the server gets a request it parses the string representation of the closure, looks up the free variables and calls the function.
(define (invoke-closure closure)
(let ((fn <lookup thing before "/" in *functions*>)
(arg <lookup thing after "/" in *free-variables*>))
(fn arg)))
This solves the problem of allocating closures but the global hash table keeps growing. This may not be a problem in the average case. Consider the usual data in free variables: front page links, users, etc. These things have to be stored on the server anyway. For temporary data that isn't stored permanently you can use a seperate hash table that is cleaned up every x minutes or after it gets too big (just like the closures you're storing now).
What do you think? I'm sorry if this explanation isn't clear. I'm not good at explaining these things, especially in english.
- pg 18y agoMaking printed representations of closures is a very hard problem. I wouldn't say insoluble, if you were willing make compromises in the language semantics, but one sign of its hardness is that all Lisp implementations I know of punt on it. You could write a whole PhD dissertation on this topic. My gut tells me it probably wouldn't be worth it. Closures don't feel like something one should expect to be representable. But I'm not 100% certain.
- pc 18y agoYou might have a workable compromise if you cheat and only serialize the portion of the closure's environment that refers to some simplified set of types. A way of serializing closures such that they kept bindings to symbols, numbers, strings and conses in the local environment would probably be pretty useful. (On the web, I think you usually want to preserve fairly simple values - usernames or whatever.) map-closure would make it easy to do at the language level, too.
- pc 18y agoAfter looking through the News.YC source, it seems that almost every single closure that's created on the fly refers to one of the above types. (I think you could exclude conses, too, to make it even simpler.)
- pg 18y agoI suppose it wouldn't be too gross to have an operator where you explicitly specified a list of variables you were going to refer to. (IIRC one had to do that in Franz Lisp.) It would only add one expr to the code I'd had before. Maybe I'll try it.
- pc 18y agoNot sure if you're intentionally proposing something different, or if I wasn't clear, but I didn't mean you should have to list the vars you intended to later refer to -- I think the serialize operator should automatically serialize all values in the local environment that are of the allowed set of types.