Subject: Re: OT: Rails is shitty
From: rpw3@rpw3.org (Rob Warnock)
Date: Fri, 25 Jan 2008 00:30:09 -0600
Newsgroups: comp.lang.lisp
Message-ID: <Ss-dnUV-g_psHATanZ2dnUVZ_rignZ2d@speakeasy.net>
[Note: Different "Rob" responding here... ;-}]

<nagler+blackhole@bivio.biz> wrote:
+---------------
| Maciej Katafiasz <mathr...@gmail.com> wrote:
| > Rob: how does bOP fit into the above? Does what I say make sense to you?
| 
| It makes sense, and if we could solve the halting problem, we could do
| what you want.  Since you can't trust the client, you have to validate
| the program that comes back.
| 
| bOP takes a limited approach.  The stack is limited to a specific set
| of parameters which are well known (task, realm, query, path info,
| form data) which can be validated easily, since that's what happened
| the first time around.  We don't encrypt this data, since it is was
| probably sent from the client anyway.  (We never give the client
| something that it didn't give you or couldn't get otherwise.)
| 
| For proper closures, you'd have to store actual code.
+---------------

Actually, you don't. What it a closure, anyway? It's just a tuple
of a function and its lexical environment. The code for the function
is (effectively) static within the application, is probably compiled
[at least for Lisp], and making multiple closures with the same
function code doesn't require copying the code for the function.
So you can safely send just the lexical environment portion of
the closure as "data" to the user's browser [I tend to use POST
forms with <INPUT TYPE='hidden'> data for that instead of encoding
it into a GET URL] and get it back when the form gets submitted.
You can recreate the original closure if you could only create
a suitable lexical environment and close the original function
over it.

Enter the technique of <http://en.wikipedia.org/wiki/Lambda_lifting>
a.k.a. closure conversion. "All you have to do" [yes, I know I'm
oversimplifying horribly] is lambda-lift the function from the
original closure [*once*, not every time you create a new closure],
externalize the values of the free variables at the time of closure,
and send the externalized values in the web page as hidden data
in a form (or forms), plus one additional tag to indicate *which*
of the many lambda-lifted functions should be called when the
response comes back [when the user submits the form]. When it does,
a dispatch function in your forms processor validates the returning
data by some appropriate means[1], and then calls the lambda-lifted
function, within which call the (newly-)bound (formerly-free)
variables will have the same values as when the original closure
was captured. Simple.  ;-}

[1] Yes, the lambda-lifting operation needs to be provided with
    enough meta-information so that it know not only how to
    externalize the current values of the function's free variables
    but also how to validate those values when they come back from
    the user's browser. A cryptographic hash (such as an HMAC) of
    the closure data in the form data pretty much takes care of the
    validation part, so the client can't ever get you to respond to
    a closure environment you didn't send it first. On the other hand,
    validating the *variable* input data the client is *supposed* to
    provide is a much harder problem.

+---------------
| You can't guarantee that the code will terminate.
+---------------

Yes you can, since the function itself (which you wrote) is never
sent to the user -- only the closure environment data.

+---------------
| You could do run-time limits, and perhaps that's good enough for
| typical UI code. You would also have to sandbox the code.
+---------------

No you don't, since the code was/is only on the server, inside
your application. You *do* need to sanitize/validate the closure
data values coming back from the user, though, as well as any
user-provided variable input. See [1] above.

If you don't want to have to fake up more-or-less real "continuations"
to run the lambda-lifted functions + data (closures) in, you can of
course CPS-transform your application manually, such that *every*
user request goes through an OLTP-like dispatcher which simply runs
the proper (lambda-lifted) global function with "the right" args.
[That's the way I tend to write my Lisp-based server-side applications.]
But even just having the *idea* of closures in your head when doing
so makes it a lot easier to structure the user interactions. It lets
you keep all that ugly "session" data [e.g., state of "shopping carts"]
out of the server, which helps scalability. It lets the user explore
alternative interaction paths in multiple browser windows without
unwanted "leakage" between them -- which cookies don't! [Sometimes
you *want* a little bit of controlled "leakage", of course, such as
a quiz where you let the user try as many paths as they want, but
then only let them "submit" one of them for credit.]

Anyway, I'm just trying to point out that the only part of a closure
you need to send/retrieve from the client is the closure's lexical
environment (hopefully externalizable data), *not* the code for the
function itself.


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607