Hi Tomas, > > No, sorry, this is not a solution to the task. It doesn't use a first > > class environment. > > yes, that's what I said in the previous email. However, the part > starting with SYMBOL-MACROLET is _the_ interesting part, which you > skipped. I probably didn't explaing myself well, sorry.
No problem. I understood your code very well, and also know what SYMBOL-MACROLET does. I just wanted to say that your "1st try" is not a solution to the task. > > 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*. > > Yes, that's in the 1st try example to understand what is the task about. No, that task is not about about binding symbols to a list of values. So this "1st try" more confuses things than it explains. > The second and third example are representing the environment exactly > like in PicoLisp. Right. > > 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. > > Yes, that's what the second and third example does. SYMBOL-MACROLET Yep. That's why I wrote: > > OK, this comes much closer. It correctly passes an environment. > allows me to do it. Think of it this way: in PicoLisp, how the > environment is hardcoded to be alist and binding lookup mechanism > assumes this. However, in Common Lisp, I used SYMBOL-MACROLET to > substitute symbol occurrences with some code, which can do the symbol As I said, I completely understood your code. SYMBOL-MACROLET is analog to 'job'. But that's no reason to worry, it is just that the _task_ was not to simulate 'job'. That's all. The only criticism in my mail was that your handling of environments is not as pure as in PicoLisp. You have to introduce the explicit variables for the binding in your code as the first argument to 'job'. So there is not really a separation of environment and code, i.e. instead of just (job <environment> <body> ) you need to write (funcall (job (var1 var2 var3 ...) <body> ) <environment> ) This makes it impossible to abstract away the 'job' and put it into a library, as is the case in the PicoLisp GUI. > Notice, the second example is written exactly like in PicoLisp (minus > the argument declaration): The task was not to do the same as PicoLisp. As far as the RosettaCode task is concerned, your solution is OK, I think. > SYMBOL-MACROLET allowed me to implement 'job' with the same semantics > like in PicoLisp. The only difference is that that I declare, at the > point of usage, the bindings which I am going to use. However, that That's what I meant above. > doesn't change much on the ability to create, save, load, send over the > wire the environment, same as in picolisp. I declared the bindings Right. > there to inform the compiler which would otherwise try to automatically > find the bindings in the default lexical environment. Also note, that > my job macro has the same apriori information about the environment as > the 'job' fexpr in Picolisp, i.e. it is an alist with symbol value pairs > that are "interpreted" as variable bindings. Yep. > Lisp, I could also implement the job macro without that declaration, > because I could walk the tree representation of the code of job's 'prg' > and find the free variables. However, although it would be automatic, > it would be like defining a function and not saying what arguments it > accepts. This way, I state my intentions and the compiler checks them. > > In short, SYMBOL-MACROLET allows me to do the same trick as 'job' uses OK, I see. This would indeed be a complete solution. (If we ignore for the moment how much this increases the terrible overhead that a macro already has) > with the same semantics. It has nothing to do with dynamic scope. Well, in some way it has. I would call such an effort (inspecting the code, collecting the variables and building the bindings) also quite "dynamic", and definitely not "static", though it happens at compile time and not at runtime as in PicoLisp. It is definitely no longer "lexical" in the sense of "scope is the program text of the function definition" (WP). This is so by definition, as the variables in the environment typically come from somewhere else, from outside of that scope. SYMBOL-MACROLET is a way to _bypass_ the restrictions of lexical scope. It is in CL for the same reason as there are "special" (dynamically bound) variables: Such features are needed. But they are there to circumvent the lexical paradigm. > Here a brief sketch of job without declaring the arguments: > ... > (defmacro job (env &body body) > ... > where FIND-FREE-VARIABLES would walk the body tree structure and return > a list of the variables that are free. OK, thanks. Good to know. As far as the RosettaCode task is concerned, I think it would be more confusing than helpful perhaps. > > 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. > > Does your function: > > (de disable (Flg) > (and Flg (=: able)) ) > > violate the separation of binding the symbols in the environment and the > actual code? No. It doesn't involve any binding at all. It uses a _property_ of the dynamic environment. > Then wouldn't it be better to always write: > > (de disable > (and Flg (=: able)) ) ?? > > 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) ) ) > > Yes, that's what closures do, they hold on to the state across > invocations. But: Can you use closures with environments read from a database, or communicated across an RPC call? > It's also what classes do btw. There's no magic about it. I didn't say there is any magic. It is just practical. A closure (like your original (job (var1 var2 ..) ..)) involves more noise, and classes introduce also some overhead, like defining them. In some cases, passing an environment around is just simple and convenient. That's what it is all about in PicoLisp. Keep it simple! You can do everything in CL you can do in Pil, of course, but it usually looks bigger and uglier (check the other CL solutions in RosettaCode). > > 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. > > Yes, again there's nothing that lexical binding prevents here. Can the dialog run in a different process? > One could say that in PicoLisp one is in fact > simulating lexical environments manually with 'job'. Perhaps, though I would not say it this way. The actual binding is still dynamically. It is just a manual control of the bindings, as you can do in a similar way with 'bind'. The key point is that this is explicit, while a lexical enviroment is implicit, not under direct control of the programmer. > > 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. > > Same holds for my code. It has nothing to do with dynamic scope;-) You You took this out of the context. My statement above didn't refer to your code, but to the dialog example. This would not work (i.e. with a lexical closure) if RPC is involved. Your implementation with SYMBOL-MACROLET would work, of course, but this was not the question. > > But I think it can't be done right with lexical scope. > > I disagree. I just showed you how:-) I don't accept this (see above, about the '"lexical" in the sense of "scope is the program text of the function definition" (WP).') > > 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. > > You missed the most important part, the SYMBOL-MACROLET part. That > changes the meaning of the var1... symbols from "symbol representing a > variable" to "symbol representing access to something". That's picky. Syntacially, (job (var1 var2 var3 ...) is like 'let' or 'lambda' (let ((var1 <val1>) (var2 <val2>)) <body> ) (funcall (lambda (x y) (* x y)) <val1> <val2>) > It's all down to having or not having a compiler. True. The compiler imposes so many restrictions upon Common Lisp, and makes it such a heavy and inflexible dinosaur. Cheers, - Alex -- UNSUBSCRIBE: mailto:firstname.lastname@example.org?subject=Unsubscribe