Hi Alex, > The following example is conceived, a normal application would not use > 'X' and 'Y' in that way, but other parts of the form's runtime > environment are indeed following this schema. But this example may show > what I mean by "separating activation of an environment from its > execution": > > # Activation > lib/form.l > (job (: env) # Here must be SYMBOL-MACROLET, as 'env' contains > ... ) # 'X' and 'Y' (unknown to 'form'). There is no body > # visible using 'X' or 'Y', that's only in the app > # Execution > myApp/gui.l > (de myButton () # The button assumes to run in a certain > environment > (gui '(+Rid +Able +Button) > '(and X Y) # Enable/disable expression > '(foo X Y) ) ) # Action expression > > # Usage > myaAp/mypage.l > (gui '(+TextField) ...) # The application code sets up the environment > (myButton) > (gui '(+TextField) ...) > > Three different files are involved. "lib/form.l" is the system library > building forms and dialogs, "myApp/gui.l" is the application's library > which defines a button to be used in several parts of the application. > > In the usage, the variables 'X' and 'Y' are free. You can't handle that > lexically.
True, the way X and Y are used here assumes dynamic binding. If I wanted to use lexical binding for X and Y, I would have to write it differently and tell the compiler more about the meaning of the sexp representing form actions. > Yes. But that's not the point. As I said, you don't need first class > environments at all if you must carry the variables explicitly with you. > You can simply use a 'let' or a 'lambda'. The issue of declaring the variables is something else, I think. If I want to save/load the environments or send them over the wire, then they must be first class environments no matter if I declare the variables or not, mustn't they? >> I could implement 'job' like this: >> >> 1 (defmacro job (env args &body body) >> 2 (let ((x (gensym))) >> 3 `(let ((,x ,env)) >> 4 (let (,@(mapcar (lambda (a) `(,a (cdr (assoc ',a ,x)))) args)) >> 5 (prog1 (progn ,@body) >> 6 ,@(mapcar (lambda (a) `(setf (cdr (assoc ',a ,x)) ,a)) >> args)))))) > > You see? You don't separate 'job' from the 'body'. > > In "lib/form.l" in PicoLisp, the body to 'job' is the huge body of the > form generating code. The application level code isn't even loaded at > that time, and is different for each form or dialog invoked. I don't understand this argument. If the application level code isn't even loaded, then there is nothing to be macroexpanded and compiled;-) 'job' is written in a separate library. With macro as above, when compiler compiles application code, the place where job is writen gets transformed before the actual compilation, and this happens at compilation time. In PicoLisp case, when the interpreter interprets application code, the place where job is written gets interpreted at run-time. Or maybe, by separate 'job' from the 'body' do you mean that simply not declaring the variables in body? Then we are back full circle to automatically detecting free variables. > Of course, you can handle some kind of environment in that way. But > you won't do that practically, because it is clumsy and not helpful, > and doesn't provide the convenience it has in PicoLisp. In Common > Lisp, it doesn't lend itself to that way, resulting in a different > programming style (I don't say this is bad, that's a matter of taste). Well even in Common Lisp I could do similar thing by declaring the free variables as special (dynamic) in the 'job' macro and then I would not need to declare them in the form action code. But still, it might be a good thing to declare the variables. Looking through the form.l code, it seems to me that the UI model could be significantly simplified. For example, are the hidden fields necessary? Is it necessary to pass binding values in url? I don't think so because all that information that is passed between http requests via query parameters in those cases is already known to the server. Take for example the *Tab variable. The server knows which tab the user selected simply by knowing which link he clicked. Same with *Menu and ID. So the only thing which is needed is simply numbering the links and associating actions (functions without arguments run in the right environment) with them. For post, the actions would be setters (functions with one argument run in the right environment). After the chosen action (and setters if it was post request) were run and the environment was modified accordingly by those actions and/or setters, the gui can be simply rendered into html. > Note that the PicoLisp style of building many GUI functions like > 'action', 'form', down to '<h1>', '<table>' and '<row>' would not be > feasible with macros, at least not efficiently, because these > functions are nesting extremely deep on a typical page. If they were > all macros, you would ge a really _huge_ code explosion, if each of > these macros gets expanded. Here is an counter-example: there are a few libraries that do exactly that and compile to a very efficient code, e.g. cl-who: (show-html-expansion (s) (:html (:body :bgcolor "white" (:table (:tr (dotimes (i 5) (htm (:td :align "left" (str i))))))))) expands to (PROGN (WRITE-STRING "<html><body bgcolor='white'><table><tr>" S) (DOTIMES (I 5) (PROGN (WRITE-STRING "<td align='left'>" S) (PRINC I S) (WRITE-STRING "</td>" S))) (WRITE-STRING "</tr></table></body></html>" S)) Also note that macroexpansion doesn't necessary mean "code explosion", the expanded sexp gets compiled, possibly to a very efficient code. But personally I think this a pointless optimization. Cheers, Tomas -- UNSUBSCRIBE: mailto:email@example.com?subject=Unsubscribe