Introducing lisp.js: a Lisp for node.js(lisp-js.posterous.com) |
Introducing lisp.js: a Lisp for node.js(lisp-js.posterous.com) |
And the source is available here (and as an NPM package):
The value that Parenscript adds here is the same as for programs that run in a web browser: you write your JS in a Lispy way at a higher level of abstraction, mostly because you get the full power of Lisp macros. There are other wins too, but that's the big one. The runtime environment, though, is entirely JS, because PS is a compiler that targets JS.
So I still don't see a difference in intent here between what PS is good for and what you're doing. I don't want to discourage you at all. Just curious about the differentiator.
Can you talk more about this decision?
Apropos, I've written a Lisp->JS compiler a while ago; you might find some of the code useful: http://github.com/manuel/cyberlisp/
The client watching references on the server is obviously more tricky. node.js already supports keep-alive by default. There probably needs to be some kind of chuncked reply mechanism that allows the server to send changes without the client having to send polling requests. Obviously, if the reference is not going to change frequently it might be cheaper to just poll every once in a while.
Lisp-1 vs Lisp-2 actually has very little to do with macros. Most of the inconvenience comes when writing functions - things like having to name list parameters "lst." The way Common Lisp gets around the variable capture problem is to forbid the rebinding of special forms, functions, and macros defined in the standard.* This is an ugly hack that forces early binding, and obviously does nothing to prevent capture of user-defined functions.
The Scheme wiki has a good article on hygiene: http://community.schemewiki.org/?hygiene-versus-gensym
* - This wasn't done for the sake of macros. The rationale behind the decision as I heard it is to prevent the hypothetical scenario of some standard function like cdr that's called inside the garbage collector or allocator from being rebound to something that tries to allocate memory and crashes the gc or sends the allocator into an infinite loop. I don't really buy it; it's easy enough to prevent these types of scenarios.
http://planet.plt-scheme.org/display.ss?package=moby.plt&...
I used it just last year — the developer, dyoo, is super responsive.
I'm sure he'd be happy to talk shop with you: http://github.com/dyoo/moby-scheme
* only requirement is node.js and the lisp source distribution, no other dependencies. * ability to debug lisp code from the browser. * immutable data structures.
I feel I can implement a lisp that can run the examples in <i>Let Over Lambda</i> in a couple of weeks. The lisp won't be as stable as Parenscript but that's ok. Only at that point can the real work start of building the web framework that is lisp.js. I think the most important bit for me is to have a base that I can modify as the needs arise. I'm already trying to include as many lisp concepts in the test source as I can find to make sure lisp.js is a general purpose lisp. In that respect I'm setting several goals: get lisp-in-lisp working, get Let Over Lambda examples working, ... any other useful lisp constructs that I come across. That's a journey I can only make if I do my own lisp implementation.
I am using Node on the backend of http://tryparenscript.com/ simply because it was easier to get up and running than Hunchentoot. Source code is on GitHub, if you want to check it out: http://github.com/fitzgen/tryparenscript.com
Here is the "Hello, WOrld!" example from the NodeJS homepage in ParenScript:
(defvar *http* (require "http"))
(chain *http*
(create-server (lambda (req res)
((@ res write-head) 200
(create "Content-Type"
"text/plain"))
((@ res end) "Hello, World!\n")))
(listen 8124 "127.0.0.1"))
((@ console log) "Server running at http://127.0.0.1:8124/")There's something satisfyingly elegant about using an interpreted language to interpret another language - macros can actually modify the compiler at compile-time, from within the language. I hope to make more use of this possibility in the future, as it's currently only used for macros.
All of the docs on Parenscript seem to assume you're using it to render bits of JS and HTML from a Common Lisp server. It would be useful if there were a tutorial that showed how to use Parenscript for this use case, simply to compile lisp-like code into standalone JS files for use with whatever arbitrary backend.
Interesting what you say about foo.bar; PS originally did that, and I was one of the people who lobbied against it. It does make getting started a little easier, but interferes with macros down the road. For example, if you want to do anything like transforming foo.bar to foox.barx, you end up having to parse the symbols to split them, which seems wrong. Since Lisp-style metaprogramming is pretty much Parenscript's raison d'être (well, that and interoperability with CL for the crazy few who care), it seems foolish to do anything to compromise it.