Hi Alex, > What I mean is to separate the _activation_ of an environment from its > _execution_. In PicoLisp, the call to 'job' (the activation of the > environment) can be in a library, not only the definition. > > Only this makes it possible, for example, to store code fragments like > (foo X Y) as event handlers, button actions or update managers in GUI > components. The library (i.e. the 'form' function) maintains > environments in all forms and dialogs, and activates them as necessary > before running those handlers and actions in the form's context. > > SYMBOL-MACROLET can't do that. It always encapsulates the > environment's activation _and_ the code body. And your defmacro'd > 'job' must therefor expand in the application domain.
I'm not sure I understand, especially the last sentence. That's what the macros do, they expand during compilation time, maybe be in application domain whatever that is. But then one could argue that fexprs evaluate in application domain at runtime. > To bring it to the point: First class environments are _useless_ if > you cannot separate activation and execution. If activation and > execution are in the same place, you can as well just pass a list of > values (without the symbols) and use 'let' or function application to > bind them to the symbols locally. > > This explains perhaps why they are not an issue in other programming > languages. At least not in lexically scoped languages. > > I think I have to write an article in the PicoLisp Wiki to explain all > that better. >From http://picolisp.com/5000/!wiki?firstClassEnvironments I understand that activation means binding/copying the values from the assoc list to the value cells of the symbols. And after execution, the values are copied back to the assoc list and original bindings are restored, if I understand correctly. Say I have this code which is same as in picolisp (except the additional explicit argument to job which we agreed could be implemented in Common Lisp by walking the sexp expression body, but lets keep it simple for now): (let ((envs (loop for i from 1 to 12 collect (list (cons 'n i) (cons 'cnt 0))))) (do () ((not (some (lambda (e) (job e (n) (< 1 n))) envs))) (dolist (e envs) (job e (n cnt) (format t "~4d" n) (unless (= 1 n) (incf cnt) (setq n (if (= 1 (logand 1 n)) (1+ (* n 3)) (/ n 2)))))) (terpri)) (dotimes (i 48) (write-char #\=)) (terpri) (dolist (e envs) (job e (cnt) (format t "~4d" cnt))) (terpri)) Creation is simple assoc consing. 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)))))) Then activation is on line 4, execution on line 5 and deactivation on line 6. I guess the 'job' fexpr is implemented something like this under the hood, i.e. bind vars to values from the assoc list, run the prg and then copy the values to the assoc list and restore original values in the previously bound symbols. Is there still something different? So with this implementation of 'job', code from the Rosetta example: (job e (n cnt) (format t "~4d" n) (unless (= 1 n) (incf cnt) (setq n (if (= 1 (logand 1 n)) (1+ (* n 3)) (/ n 2))))) expands to: (LET ((#:G17291 E)) (LET ((N (CDR (ASSOC 'N #:G17291))) ;; activation (CNT (CDR (ASSOC 'CNT #:G17291)))) (PROG1 (PROGN (FORMAT T "~4d" N) ;; execution (UNLESS (= 1 N) (INCF CNT) (SETQ N (IF (= 1 (LOGAND 1 N)) (1+ (* N 3)) (/ N 2))))) (SETF (CDR (ASSOC 'N #:G17291)) N) ;; deactivation (SETF (CDR (ASSOC 'CNT #:G17291)) CNT)))) > But it also confirms my initial statement, that proper first class > environments (handling creation, activation and execution > independently) cannot be done with pure lexical binding. True, not with pure lexical binding. There are two dimensions to the problem here: one goes along the fexpr/macro/function axis and the other along dynamic/lexical scope axis. And first class environments like in PicoLisp can be done with lexical bindings if you use macros. > These examples don't show the separation of activation and execution, > as this cannot be simply done in an isolated example. For a real-world > use case, take a look at the top-level GUI function in > "@lib/form.l". The relevant part can be reduced to That's not helpful for understanding the issue, it should be possible to write very simple example. All the http noise could be taken away as it adds nothing informative here. It could be simplified into a simple function call, which it basically is. What is important is that the function is a closure, it works on an environment, remembers values across invocations and changes the environment accordingly. The simplest example for this kind of stuff is something like: (defun gui () (let ((n 0)) ;; some state (lambda () (incf n)))) (setq *App (gui)) (funcall *App) => 1 ;; 1st http request (get or post doesn't matter), e.g. click (funcall *App) => 2 ;; 2nd http request (funcall *App) => 3 ;; 3rd http request Your example at the end of http://picolisp.com/5000/!wiki?firstClassEnvironments is basically such a closure invocation. Cheers, Tomas -- UNSUBSCRIBE: mailto:[email protected]?subject=Unsubscribe
