Re: Questions about EmuLisp
Hi Christophe, > These are primarily questions for Alex, but I'm interested > in the answers of other people too. I'm afraid that I don't have useful answers, but I try ... > 1) What do you think of EmuLisp? I like the fact that it is written in JS, but I don't know its limitations. > 2) Would you promote it? > 3) Would you use it on the futur website for the demo REPL? Personally I prefer server-side application code, as it gives more power to the application, and also makes development a lot easier. So it is not an option for me at the moment. On the client I prefer hand-crafted JS, which doesn't need to be touched during application development. ♪♫ Alex -- UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe
Re: Questions about EmuLisp
Hi Christophe. AFAIK ClojureScript is pretty much feature complete as opposed to Emu, there's a risk for confusion and perhaps bad PR if an online REPL based on Emu becomes the de facto way of playing around with PL for novices, ie "Why can't I do X which the manual seems to imply?!!" or "This crap sucks, it can't even do Y." when it in fact can do Y. Some prominent disclaimer should be there, perhaps with information on what features are not available. The fact that PL is now apt-get installable makes it trivial to test out the real thing too. On Sat, Jan 2, 2016 at 9:40 PM, Christophe Gragnicwrote: > Hi all, > > These are primarily questions for Alex, but I'm interested > in the answers of other people too. > > I'm a big fan of PicoLisp even if I only scratched the surface yet, > since I don't use the server or the DB. > I know most of the heavy PicoLisp users here rely extensively > on the server and the DB and wouldn't use Ersatz for real things. > So bear with me for questions about EmuLisp, > which is even "weaker" that Ersatz! > > 1) What do you think of EmuLisp? > 2) Would you promote it? > 3) Would you use it on the futur website for the demo REPL? > 4) I'm beginning a journey to the understandings of > Clojure/ClojureScript and would love to see something > like this for PicoLisp. Does this ring a bell for someone? > > For your information, there is now a NodeJS module: > https://www.npmjs.com/package/emulisp > > > chri > > -- > > http://profgra.org/lycee/ (site pro) > http://delicious.com/profgraorg (liens, favoris) > https://twitter.com/profgraorg > http://microalg.info (langage de programmation pédagogique) > http://expressions.club/ (structure des expressions mathématiques) > -- > UNSUBSCRIBE: mailto:picolisp@software-lab.de?subjectUnsubscribe -- UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe
RE: questions about the gui
Yes, this helps, I did not understand that psh was a debugging tool. About (app), I still would like to know, if there was a way to run more than one application on the same computer and be sure that they would not by accident decide to use the same port. I understand that within one app there is no problem (as long as there are not too many users), but with two or more? Or do my network programming memories need a refresh? Denis > From: johtob...@gmail.com > To: picolisp@software-lab.de > Subject: Re: questions about the gui > Date: Sat, 21 Nov 2015 08:51:43 +0100 > > > Hello, > > It is a local shell that connects to a local webserver. > It can be used for webdevelopment, you can expect variables and so on. > (app) open a new process with a new port. So each process and each > single user linked to it can has a own state. The user can connects to > his new port > Does it help? > > Am 21.11.2015 08:28 schrieb "Denis Fourt" > <denis.p...@hotmail.com<mailto:denis.p...@hotmail.com>>: > Hello, > After reading the gui documentation, I would like some help on the > following topics, please : > a) I am not sure to understand the purpose and the uses of the psh function -- UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe
Re: questions about the gui
Hi Denis, in addition to what Joh-Tob said, let me correct some issues about 'app'. > b) I understand that calling the (app) function allows multiple users to > access > an application at the same time, which makes web apps and collaborative > software > possible What (app) really does is establishing a "session". When a client (browser) connects to the server, the server forks a child process which sends a response to the request. This is typically a GET request, and the child sends a HTML page to the client. At the same time, the server parent process continues to listen for further requests. Now, when (app) is NOT called in the child while it generates its response (i.e. it sends a static page), then the child process terminates. If, however, (app) is called, the child does not terminate. It allocates a new port to listen for further requests from that client, allows login, keeps the session's state, and so on. So multi-user access to the application is also possible without (app), but each request will be answered in a fire-and-forget style. > a process listening on different port is created for each user isn't it? right, this is what is happening. > So how do you avoid conflict when running independent applications? To have more than one application running on a single machine, you start several server parent processes. Each of them will be independently listening on its own port. We use 'httpGate' as a port proxy, so that from the browser's view the port is always 80 (HTTP) or 443 (HTTPS), but is relayed on the server to the right port (and thus server process). > In case of single user desktop apps, not calling (app) and reserving a port > seems sufficient. But in case of several users? So, as you see, (app) has nothing to do with single- or multi-user. Also, a single application will need (app) to allow sessions, and this in turn has nothing to do with how many users access this application. ♪♫ Alex -- UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe
Re: questions about the gui
I assume you fear that a port gets binded to a socket twice. That can not happen, since unix/linux does not allow two processes to bind the same process. (app) calls the pil function (port). The port function itself is able to find a unused port an binds it. For more details read (doc 'port) If you are good with C you might want to look inside net.c and understand doport. Picolisp takes care of the ports for you. 2015-11-21 9:36 GMT+01:00 Denis Fourt <denis.p...@hotmail.com>: > Yes, this helps, I did not understand that psh was a debugging tool. About > (app), I still would like to know, if there was a way to run more than one > application on the same computer and be sure that they would not by > accident decide to use the same port. I understand that within one app > there is no problem (as long as there are not too many users), but with two > or more? Or do my network programming memories need a refresh? > > Denis > > > From: johtob...@gmail.com > > To: picolisp@software-lab.de > > Subject: Re: questions about the gui > > Date: Sat, 21 Nov 2015 08:51:43 +0100 > > > > > > Hello, > > > > It is a local shell that connects to a local webserver. > > It can be used for webdevelopment, you can expect variables and so on. > > (app) open a new process with a new port. So each process and each > > single user linked to it can has a own state. The user can connects to > > his new port > > Does it help? > > > > Am 21.11.2015 08:28 schrieb "Denis Fourt" > > <denis.p...@hotmail.com<mailto:denis.p...@hotmail.com>>: > > Hello, > > After reading the gui documentation, I would like some help on the > > following topics, please : > > a) I am not sure to understand the purpose and the uses of the psh > function > -- > UNSUBSCRIBE: mailto:picolisp@software-lab.de?subjectUnsubscribe >
Re: questions about the gui
Hello, It is a local shell that connects to a local webserver. It can be used for webdevelopment, you can expect variables and so on. (app) open a new process with a new port. So each process and each single user linked to it can has a own state. The user can connects to his new port. Does it help? Am 21.11.2015 08:28 schrieb "Denis Fourt": > Hello, > After reading the gui documentation, I would like some help on the > following topics, please : > a) I am not sure to understand the purpose and the uses of the psh function
Re: Questions
Hi Alex, Only when an object is accessed (value, property list etc.), it is automatically loaded into the Lisp heap as an external symbol. And once loaded, they can be garbage collected when needed, right? The current database files are in a binary format (called PLIO for PicoLisp I/O). This is the same format as written and read by the 'pr' and 'rd' functions. And try following and have no success (the database file is still in binary). (let (rd read pr print) (pool /tmp/t.db) (set *DB Hello world) (put *DB 'a 11) (commit) ) Best regards, KS -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Henrik, When you have a database of several hundred GB, have you used the ability to split it up? If so why and how, what considerations went into the splitting decision? Yes, split up in several types of application that communicate with each other, where each application has its own DB (split into several files, of course). These applications run on a number of blades, each with multiple CPUs. The two biggest DBs we have at one customer, one with 250 million and one with ore than 300 million primary items. As each item is represented by about 7 database objects, plus some auxiliary objects and a lot of indexes, the total number of external symbols at that customer is around four billion. I recall faintly that you had to implement remote database functionality at one point (I even think that discussion is in the mailing list somewhere). Was that done for this very project which is using the big DB? Yes, this is what triggered that feature. There is a central application that queries the other DBs. Cheers, - Alex -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Kriangkrai, Ah, Thanks. Could you please explain how a double value is encoded in cell? I could not decipher doubleToNum() and numToDouble() in big.c! As PicoLisp only handles scaled fixed point numbers, the double is encoded as a number, with the additional scale passed to the functions. In general, a number is encoded in cells as Number | V +-+-+ +-+-+ +-+-+ |'DIG'| ---+--- |'DIG'| ---+--- |'DIG'| / | +-+-+ +-+-+ +-+-+ where each digig 'DIG' holds 32 bits of information (in the 64 bit version, there are also 60 bit short numbers, and the bignum cells naturally hold 64 bits). The sign is in the lowest bit of the first digit. So what numToDouble() does is: Take that number, scale and add up all the digits, and finally set the sign. doubleToNum() does the opposite, by fitting a (sufficiently large) double number into the above cell structure. : *Scl - 0 : (let *Scl 2 1.23) - 1 Is this because 1.23 is parsed when *Scl is still 0, right? Correct. '*Scl' only applies to input (the reader). It is usually set globally, at the beginning of an application, e.g. with (setq *Scl 2). In general, it is not so very useful and important, as the I/O format will always depend on the local context. Is there a better way than using: : (let *Scl 2 (format 1.23 *Scl)) Probably not. Typically, such cases will not use the global '*Scl', but some value from the database or some other context. For example, the numeric input fields in lib/form.l each have their own local scale. The other parameters to 'format' (the decimal and thousands delimiter) are usually taken from the current locale. 1. What does 'mis' stand for? In the sense of negation (as in misfit or misfortune). This message is sent to relation objects to check if a given value will fit. If not, the GUI issues an error message. Most of these methods are defined in lib/db.l. 2. Why a reverse association list (instead of a normal association list) is used for the property list? : (putl 'X '((10 . a) (20 . b))) (show 'X) This has advantages if you access these cells directly, with 'prop' or '::'. While : (get 'X 'a) - 10 will give the value, : (prop 'X 'a) - (10 . a) returns the whole cell. This can be used with all functions that accept a 'var' argument (a 'var' is either a symbol or a cell): : (inc (prop 'X 'a)) - 11 : (get 'X 'a) - 11 Thanks for your time answering my questions. No problem :-) Cheers, - Alex -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Kriangkrai, A tutorial on PicoLisp FFI would be great; with that, PicoLisp would have no shortage of libraries! ;-) not exactly a tutorial, but I have looked into this a while ago and you can find a prototype ffi generator at http://logand.com/mplisp/src/mod/ It should be fairly straitforward to understand how different types get converted between picolisp and C, e.g. http://logand.com/mplisp/src/mod/ffi.l is the ffi generator and http://logand.com/mplisp/src/mod/gl.ffi is sample ffi binding for OpenGL. Cheers, Tomas -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Kriangkrai, Because with (@ attr1 val1 ...), you can retrive the attributes by just using (assoc '@ sxml). Is there any advantage of using your syntax @attr1 val1 ...? advantage is that you don't have to put the attributes in a list manually, your 'with-xml' function can do that for you;-) Also, if you don't manage to hook your error handler to define the tag functions on-the-fly, you can always traverse the tree argument of 'with-xml' and replace (or define) the tag symbols with your generic tag function and then eval the whole tree. Good idea. Thanks :-) On the other hand, 'with-xml' will then waste lot's of time traversing all those trees (unless you get somehow clever, memoize the replaced tree) so I would recommend to use @lib/xhtml.l if you use 'built-in' html components or @lib/xml.l if your xml tags can be anything. With your original approach, there is still danger Henrik emphasized, that you can overshadow existing functions. The libraries above do not have this problem. Cheers, Tomas -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Kriangkrai, Is there a way, to make the code below works, the first time 'foo' is defined? : foo - NIL : (mydefs (+ 1 (foo 3 4))) # should returns 20 As I said, not without modification of the low-level error handler. Execution is not continued after the error is caught, as the 'catch' is in 'mydefs' and not on the lowest level. Why would you need that? If you want a mechanism just to load a certain set of functions on the fly (like those with a colon for the built-in dlopen/dlsym mechanism), you could do something like: In a file foo.l: (de foo (X Y) (+ X (* Y Y)) ) and then define those functions like (de on-the-fly (@Fun @File) (def @Fun (curry (@Fun @File) @ (undef '@Fun) (load @File) (pass @Fun) ) ) ) (on-the-fly 'foo foo.l) (on-the-fly 'bar bar.l) then, 'foo' looks initially like : foo - (@ (undef 'foo) (load foo.l) (pass foo)) Now if you execute it : (foo 3 4) - 19 it will be redefined : foo - ((X Y) (+ X (* Y Y))) Is PicoLisp64 just a 64-bit version of PicoLisp? Or it will have more/different features? (I would request floating-point support ;-) It will be (I hope) totally compatible. Floating point support is not an option (perhaps you might want to look in the archive of this list, we had a discussion about that). The additional tag bit received by the extension to 64 bits is better used for a short integer, and doing otherwise would break some fundamental assumptions. Also, floating point calculations are better done separately (there are C functions for that) or - recommended - in scaled fixpoint arithmetics (a good example for that is the flight simulator). Cheers, - Alex -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Alex, As I said, not without modification of the low-level error handler. Execution is not continued after the error is caught, as the 'catch' is in 'mydefs' and not on the lowest level. Why would you need that? It will enable code like this: (with-xml (tag1 (@ attr1 val1) text (tag2 text) text )) 'with-xml' would be defined to overwrite the undefined error handler in its dynamic scope to recognize calls whose CAR starts with ''. Also, floating point calculations are better done separately (there are C functions for that) Do you mean using FFI, i.e. calling C functions from Lisp? If so, how? Could you give me an example for calling C's double sin(double x) in math.h. Best regards, KS -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Kriangkrai, Also, floating point calculations are better done separately (there are C functions for that) Do you mean using FFI, i.e. calling C functions from Lisp? If so, how? Could you give me an example for calling C's double sin(double x) in math.h. Please take a look in src/ext.c. There is, for example, 'ext:Sin', that interfaces to scaled integers: : (ext:Sin 314159 10) - 0 : (ext:Cos 314159 10) - -10 If you set '*Scl', you can make it a little more readable: : (setq *Scl 5) - 5 : (ext:Cos 3.14159 1.0) - -10 : (ext:Sin 0.5 1.0) - 47943 : (format (ext:Cos 3.14159 1.0) *Scl) - -1.0 : (format (ext:Sin 0.5 1.0) *Scl) - 0.47943 Cheers, - Alex -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Kriangkrai, (with-xml (tag1 (@ attr1 val1) text (tag2 text) text )) If you go this route, why not something like: (with-xml (tag1 @attr1 val1 text (tag2 text) text ) ) Also, if you don't manage to hook your error handler to define the tag functions on-the-fly, you can always traverse the tree argument of 'with-xml' and replace (or define) the tag symbols with your generic tag function and then eval the whole tree. Cheers, Tomas -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Tomas, =A0 =A0(with-xml =A0 =A0 =A0 (tag1 (@ attr1 val1) =A0 =A0 =A0 =A0 =A0text =A0 =A0 =A0 =A0 =A0(tag2 text) =A0 =A0 =A0 =A0 =A0text )) If you go this route, why not something like: (with-xml =A0 (tag1 @attr1 val1 =A0 =A0 =A0text =A0 =A0 =A0(tag2 text) =A0 =A0 =A0text ) ) Because with (@ attr1 val1 ...), you can retrive the attributes by just using (assoc '@ sxml). Is there any advantage of using your syntax @attr1 val1 ...? Also, if you don't manage to hook your error handler to define the tag functions on-the-fly, you can always traverse the tree argument of 'with-xml' and replace (or define) the tag symbols with your generic tag function and then eval the whole tree. Good idea. Thanks :-) Best regards, KS -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Alex, Please take a look in src/ext.c. There is, for example, 'ext:Sin', that interfaces to scaled integers: =A0 : (ext:Sin 314159 10) =A0 - 0 =A0 : (ext:Cos 314159 10) =A0 - -10 Ah, Thanks. Could you please explain how a double value is encoded in cell? I could not decipher doubleToNum() and numToDouble() in big.c! A tutorial on PicoLisp FFI would be great; with that, PicoLisp would have no shortage of libraries! ;-) If you set '*Scl', you can make it a little more readable: =A0 : (setq *Scl 5) =A0 - 5 =A0 : (ext:Cos 3.14159 1.0) =A0 - -10 =A0 : (ext:Sin 0.5 1.0) =A0 - 47943 =A0 : (format (ext:Cos 3.14159 1.0) *Scl) =A0 - -1.0 =A0 : (format (ext:Sin 0.5 1.0) *Scl) =A0 - 0.47943 By the way, is the result of following code expected? : *Scl - 0 : (let *Scl 2 1.23) - 1 Is this because 1.23 is parsed when *Scl is still 0, right? Is there a better way than using: : (let *Scl 2 (format 1.23 *Scl)) - 123 More questions ;-) 1. What does 'mis' stand for? 2. Why a reverse association list (instead of a normal association list) is used for the property list? : (putl 'X '((10 . a) (20 . b))) (show 'X) X NIL b 20 a 10 - X Thanks for your time answering my questions. Best regards, KS -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Kriangkrai, 1. What is the purpose of calling (wait) after (server 8080 ...)? It will wait for what? What is the differences if (wait) is not called? In this case (when called without any arguments), 'wait' will wait for an infinite time (see also doc/refW.html#wait). The purpose is to inhibit PicoLisp from dropping into a read-eval loop when 'server' received a connect. This is opposed to debugging mode where you don't call that 'wait', and instead want to debug the GUI session. 2. Could you explain how does ht: magic work? : ht:Prin - NIL : (ht:Prin xxx) xxx- T 'ht:Prin' is a normal symbol and thus has a value of NIL initially. If such a symbol is called as a function, it would usually cause an error: : abc - NIL : (abc xxx) !? (abc xxx) abc -- Undefined However, when the Undefined error handling routine of PicoLisp detects that the name of that undefined symbol contains a colon, it tries to locate a shared object library (DLL) with the name before the colon (here 'ht'), and searches for a symbol with the name after the colon (here 'Prin'). If it finds such a symbol, it defines the Lisp symbol 'ht:Prin' so that from now on it is just like any other function written in C: : ht:Prin - NIL # Not defined : (ht:Prin xxx)# Calling it xxx- T : ht:Prin - 1540706488# Now it is defined : (ht:abc) ht:abc -- /usr/local/picoLisp/lib/ht: undefined symbol: abc So this failed, as the 'ht' library does not contain 'abc'. 3. In the Alerts and Dialogs example in doc/app.html, there is a call (dispose (: home top 1)) Oops, you found an error in the documentation! The function 'dispose' does not exist any more. It is now implicit in the 'yesButton'. So the example should look like: '(alert NIL Are you sure? (yesButton '(set (: home top 2 gui 1) (val (: home top 1 gui 1)) ) ) (noButton) ) ) I fixed it, and uploaded a corrected version to the testing release. which is meant to remove enclosing form (right?), but the problem is that 'dispose' does not exist! To be precise, it is meant to remove the dialog from above the form. On a page, there may be any numbers of forms, and above these forms may be any number of pop up dialogs. These dialogs usually have some buttons like close, yes, no etc. which may or may not close them. Instead of the 'dispose' function, dialogs are now closes by the '+Close' prefix class to those buttons. For example, the 'yesButton' looks like: (de yesButton (Exe) (gui '(+Close +Button) ',Yes Exe) ) i.e. it creates a button which inherits the close behavior from '+Close', and which is labelled Yes. When that button is pressed, the expression 'Exe' is executed (the (set ...)) above, and the dialog (or alert) is closed. 4. What is 'Dn' in (dm set (Val Dn) ...)? For what? This is an internal parameter to the 'set' method of GUI components. It is not used on the application level. IIRC, it means down, and is used in charts (two-dimensional tables of GUI components) to avoid infinite recursion when setting the values of those components. This is because setting the value of such a components will trigger the setting of the value of the whole chart, and setting the value of a chart will trigger the setting of the values of its individual components. Cheers, - Alex -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe
Re: Questions
Hi Alex, 1. What is the purpose of calling (wait) after (server 8080 ...)? It will wait for what? What is the differences if (wait) is not called? In this case (when called without any arguments), 'wait' will wait for an infinite time (see also doc/refW.html#wait). The purpose is to inhibit PicoLisp from dropping into a read-eval loop when 'server' received a connect. This is opposed to debugging mode where you don't call that 'wait', and instead want to debug the GUI session. OK. Thanks. 2. Could you explain how does ht: magic work? : ht:Prin - NIL : (ht:Prin xxx) xxx- T 'ht:Prin' is a normal symbol and thus has a value of NIL initially. If such a symbol is called as a function, it would usually cause an error: : abc - NIL : (abc xxx) !? (abc xxx) abc -- Undefined However, when the Undefined error handling routine of PicoLisp detects that the name of that undefined symbol contains a colon, it tries to locate a shared object library (DLL) with the name before the colon (here 'ht'), and searches for a symbol with the name after the colon (here 'Prin'). If it finds such a symbol, it defines the Lisp symbol 'ht:Prin' so that from now on it is just like any other function written in C: : ht:Prin - NIL # Not defined : (ht:Prin xxx)# Calling it xxx- T : ht:Prin - 1540706488# Now it is defined : (ht:abc) ht:abc -- /usr/local/picoLisp/lib/ht: undefined symbol: abc So this failed, as the 'ht' library does not contain 'abc'. OK. Can the Undefined error handling be defined/extended/overridden at Lisp (not C) level? Something like Ruby's const_missing() and method_missing()? 3. In the Alerts and Dialogs example in doc/app.html, there is a call (dispose (: home top 1)) Oops, you found an error in the documentation! The function 'dispose' does not exist any more. It is now implicit in the 'yesButton'. So the example should look like: '(alert NIL Are you sure? (yesButton '(set (: home top 2 gui 1) (val (: home top 1 gui 1)) ) ) (noButton) ) ) I fixed it, and uploaded a corrected version to the testing release. which is meant to remove enclosing form (right?), but the problem is that 'dispose' does not exist! To be precise, it is meant to remove the dialog from above the form. On a page, there may be any numbers of forms, and above these forms may be any number of pop up dialogs. These dialogs usually have some buttons like close, yes, no etc. which may or may not close them. Instead of the 'dispose' function, dialogs are now closes by the '+Close' prefix class to those buttons. For example, the 'yesButton' looks like: (de yesButton (Exe) (gui '(+Close +Button) ',Yes Exe) ) i.e. it creates a button which inherits the close behavior from '+Close', and which is labelled Yes. When that button is pressed, the expression 'Exe' is executed (the (set ...)) above, and the dialog (or alert) is closed. OK. But it will close only the alert (the enclosing form of the button), not the dialog (parent form of the alert). To close the dialog, the user must press the cancel button, after the alert was closed. Isn't (dispose (: home top 1)) trying to close the dialog? Is it possible to do so? 4. What is 'Dn' in (dm set (Val Dn) ...)? For what? This is an internal parameter to the 'set' method of GUI components. It is not used on the application level. IIRC, it means down, and is used in charts (two-dimensional tables of GUI components) to avoid infinite recursion when setting the values of those components. This is because setting the value of such a components will trigger the setting of the value of the whole chart, and setting the value of a chart will trigger the setting of the values of its individual components. OK. By the way, is there a reference-style documentation for GUI (e.g. +gui, +Chart) and Database (e.g. +Entity, +Relation), something like the useful Pico Lisp Reference (ref.html)? The source code has sparse comments, if any! ;-) Best regards, KS -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe