Hi Tomas,

> > BTW, those who advocate lexical binding are suspiciously silent when it
> > comes to demonstrations of the power of dynamic binding.
> >
> > For example, I'm still waiting for lexical-bound Common Lisp or Scheme
> > solutions in
> >
> >    http://rosettacode.org/wiki/First_class_environments
> 
> Maybe because nobody has written it yet?  :-)

Exactly. That's the point.


> Ok, I'll face your challenge!  Here are my solutions in Common Lisp.

Great! Thanks! :)


> 1st try:
> 
> (let ((envs (loop for i from 1 to 12 collect (list i 0))))
>   (do ()
>       ((not (some (lambda (e) (apply (lambda (n cnt) (< 1 n)) e)) envs)))
>     (mapc (lambda (e)
>             (symbol-macrolet ((n (car e))
>                               (cnt (cadr e)))
> ...

No, sorry, this is not a solution to the task. It doesn't use a first
class environment.

Let me explain for the public (as you already know the details): The
'envs' list above is a just a normal data structure, holding a list of
*values*. A first class environment encapsulates *bindings* of symbols
and values. The important point is that you can handle whole bindings as
first class items, creating and passing them around independent of the
code that might use them in some other place.

You can, of course, get similar results by passing just the data, but
then the runtime code must have a priori knowledge about the structure,
like the order of the values. And the code must for itself declare the
variables (lambda, macrolet etc.), violating the separation of binding
the symbols in the environment, and the actual code.


For example, the PicoLisp GUI uses first class environments to pass
bindings from one frame or dialog to another, across HTTP transactions.

Consider this code fragment

   (gui '(+Button) "Do something"
      '(dialog (env '(Foo Bar Baz (Cnt . 3)) 'X (calcX))
         (<grid> 2
            ... directly using Foo, Bar, Baz, Cnt, and X ... )
         (okButton) ) )

When the button "Do something" is pressed, the environment (i.e. the
variables Foo, Bar and Baz and their current values, plus Cnt and X) is
passed through the round-trip of the HTTP POST transaction to the HTML
form building the dialog. The code in the dialog (here in the '<grid>'
body) can simply "use" these variables.

There is only a single place where these variables are "declared", the
rest is abstracted away because the whole environment is passed into the
dialog.

Though in this example the creation and usage of that environment are
still in the same lexical scope, this doesn't have to be the case. The
environment might as well be read from a file, or passed across a
network connection in an RPC call.


> I don't think this assignment has much to do with the question of
> lexical versus dynamic bindings and even not with built-in support for
> first class environment.

No, it has indeed. To my understanding, you can't (by definition)
separate variables and code in lexical scopes (see below).

Of course I never had any doubt that first class environments *can* be
implemented in Common Lisp, as it also supports dynamic binding (using
"special" variables). But I think it can't be done right with lexical
scope.


> There are a few solutions in languages that
> don't have that support and they simply create a structure and work with
> that.

You mean in the RosettaCode task? Yes, because there is no other way in
those languages.


> I actually prefer the following version:
> 
> (defmacro job (args &body body)
>   `(lambda (env)
>      (symbol-macrolet (,@(mapcar (lambda (x) `(,x (cdr (assoc ',x env)))) 
> args))
>        ,@body)))
> 
> (let ((envs (loop for i from 1 to 12 collect (list (cons 'n i) (cons 'cnt 
> 0)))))
>   (do ()
>       ((not (some (job (n) (< 1 n)) envs)))
>     (mapc (job (n cnt)
> ...

OK, this comes much closer. It correctly passes an environment.

But still it needs an explicit "declaration", with the 'args' argument
to 'job' (n cnt), making it a normal function call to set up the
environment. The code needs an a explicit (a priori) knowledge of the
variables, to satisfy the lexical requirements.

With that, a Picolisp call like

   (job <environment>
      <body> )

becomes to

   (funcall
      (job (var1 var2 var3 ...)
         <body> )
      <environment> )

which does not really taste like simply executing some code body within
an environment. This might explain why first class environments are not
an issue in CL.

In total, however, I would say it is an acceptable solution for the task
at hand. Can you submit it to RosettaCode?


The RosettaCod task as it is given, is perhaps not a typical use case
for first class environments. But it is very difficult to think up a
simple and understandable example.

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

Reply via email to