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

(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))))))
  (dotimes (i 48) (write-char #\=))
  (dolist (e envs)
    (job e (cnt)
      (format t "~4d" cnt)))

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

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.


UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe

Reply via email to