
Ask HN: Designing a Parenscript alternative - evanrmurphy
Several projects already exist which compile established dialects of Lisp into JavaScript, including Parenscript [1], Scriptjure [2], Scheme2Js [3] and ArcScript [4]. I&#x27;m trying to determine if there&#x27;d be value in creating a new little Lisp dialect specifically designed for compiling into JavaScript. The goal would be to end up with something like CoffeeScript [5], only s-expression based so that Lisp-style macros could be supported.<p>I&#x27;ve started designing the language following this general process:<p>1. Imagine what JavaScript would look like if it were composed of s-expressions rather than C-style syntax<p>2. Permit irregularities where it would be a convenience boon to everyday JavaScript coding<p>I will post example code in the comment thread, and feedback is very welcome. Do you think this project has potential, and would you consider using it? Do you have any advice about writing or hosting the compiler?<p>---<p>[1] <a href="http:&#x2F;&#x2F;common-lisp.net&#x2F;project&#x2F;parenscript&#x2F;" rel="nofollow">http:&#x2F;&#x2F;common-lisp.net&#x2F;project&#x2F;parenscript&#x2F;</a><p>[2] <a href="https:&#x2F;&#x2F;github.com&#x2F;arohner&#x2F;scriptjure" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;arohner&#x2F;scriptjure</a><p>[3] <a href="http:&#x2F;&#x2F;www-sop.inria.fr&#x2F;indes&#x2F;scheme2js&#x2F;" rel="nofollow">http:&#x2F;&#x2F;www-sop.inria.fr&#x2F;indes&#x2F;scheme2js&#x2F;</a><p>[4] <a href="http:&#x2F;&#x2F;evanrmurphy.com:8080&#x2F;arc2js" rel="nofollow">http:&#x2F;&#x2F;evanrmurphy.com:8080&#x2F;arc2js</a><p>[5] <a href="http:&#x2F;&#x2F;jashkenas.github.com&#x2F;coffee-script&#x2F;" rel="nofollow">http:&#x2F;&#x2F;jashkenas.github.com&#x2F;coffee-script&#x2F;</a>
======
mrpixel
I'm quite curious about the examples. Maybe you should prefix the title with
"Ask HN:". Write it in something lispy.

------
evanrmurphy
Sibilant (<http://sibilantjs.info/>) and lisp.js (<http://lisp-
js.posterous.com/lispjs-a-lisp-for-nodejs-0>) are two related projects.

------
mrpixel
(function() { var x; x = 5; return ++x; }).call(this);

This won't loop but just return 6.

~~~
evanrmurphy
do is intended to be like do in Arc or Clojure or progn in Common Lisp. It is
not supposed to be a loop. Does this clarify or have I misunderstood your
comment?

~~~
mrpixel
Ah, O.K. Mea culpa! :)

------
mrpixel
I'm a bit puzzled now as I don't see a real benefit comparing this to the
existing "compilers". Even worse: there's no code base.

~~~
evanrmurphy
Thank you for all your feedback so far. Is there anything about the existing
lisp-to-js compilers that you wish was different?

I've just added a piece about the dot operator to the examples. I don't think
any of the existing compilers have tried this approach.

~~~
mrpixel
AFAIR some ppl already have played around with that dot-notation. But I must
admit that I just tried Parenscript some three years ago and was completely
pissed off because it didn't have return values for everything. I want my code
to be portable. Didn't touch any of those other translators.

~~~
sedachv
"But I must admit that I just tried Parenscript some three years ago and was
completely pissed off because it didn't have return values for everything."

Yeah, we're still working on that (mostly because I'm stupid). The
expression/statement dichotomy is a wily satan.

------
evanrmurphy
Infix and unary operators are pretty straightforward, since they're just
prefix versions of their JavaScript counterparts:

    
    
      (&& x y)    => x && y
      (|| x y)    => x || y
      (+ x y)     => x + y
      (- x y)     => x - y
      (* x y)     => x * y
      (/ x y)     => x / y
      (% x y)     => x % y
      (+= x y)    => x += y
      (-= x y)    => x -= y
      (*= x y)    => x *= y
      (/= x y)    => x /= y
      (%= x y)    => x %= y
      (= x y)     => x = y
      (== x y)    => x == y
      (=== x y)   => x === y
      (!= x y)    => x != y
      (!== x y)   => x !== y  
      (! x)       => !x
      (++ x)      => ++x
      (-- x)      => --x
      (. x y)     => x.y
    

While the dot operator's base form should be a prefixed s-expression like all
the others, it may be awkward to use in practice:

    
    
      (. (. ($ "body") (addClass "hidden")) (hide))
      
      => $("body").addClass("hidden").hide();
    

Since the dot operator is so common in idiomatic JavaScript, I propose
allowing it to be infixed as a special case:

    
    
      ; expands to the above prefixed version
    
      ($ "body").(addClass "hidden").(hide)
    

For blocks we use a wrapper function rather than the curly brace block so that
lexical scoping for variables can be ensured:

    
    
      (do (= x 5)   => (function() {
          (++ x))        var x;
                         x = 5;
                         return ++x;
                       }).call(this); 
                   

The ternary operator:

    
    
      (?: x y z)   => (x ? y : z)
    

Perhaps we can create a useful distinction between the ternary operator and
if-statements by giving if implicit do (just as cond has implicit progn in
Common Lisp), but I'm not sure what the most elegant solution is. Thoughts?

Functions:

    
    
      (function)              => (function() {});
      (function ())           => (function() {});
      (function ()            => function() {
        (+ 1 1))                   return 1 + 1;
                                 };
      (= foo (function ()     => var foo;
               (+ 1 1)))         foo = function() {
                                   return 1 + 1;
                                 }
      

We could build a simple unhygienic macro system using the traditional quote
('), quasiquote (`), unquote (,) and unquote-splicing (,@) operators, as well
as rest parameters (&). Here's how you might define a macro called unless:

    
    
      ; macro definition
    
      (mac unless (x & args)
        `(if (! ,x) ,@args))
    
      ; example usage
    
      (unless true
        (alert "Won't be executed"))
    
      ; expands to:
    
      (if (! true) 
        (alert "Won't be executed"))
    
      => if (!true) {
           alert("Won't be executed");
         }
    

More coming...

~~~
sedachv
"While the dot operator's base form should be a prefixed s-expression like all
the others, it may be awkward to use in practice"

The solution isn't special syntax, it's macros. Check out Parenscript's
_chain_ :

[http://common-
lisp.net/project/parenscript/reference.html#ch...](http://common-
lisp.net/project/parenscript/reference.html#chain)

"For blocks we use a wrapper function rather than the curly brace block so
that lexical scoping for variables can be ensured."

In most cases you can generate more efficient code by doing rudimentary
control flow analysis. Look at how RETURN-FROM is implemented in the current
Parenscript repository version.

Also, speaking of lexical scoping don't forget that in JS loop variables only
get a single binding. Scheme2JS uses a neat trick with _with_ to implement the
proper (per-iteration) binding (see this paper: <http://www-
sop.inria.fr/indes/scheme2js/files/icfp2006.pdf>). It's the only useful use of
_with_ I've ever encountered.

"The ternary operator"

Don't. The #1 goal of any new Lisp to JS translator should be to eliminate the
statement/expression dichotomy.

"We could build a simple unhygienic macro system using the traditional quote
('), quasiquote (`), unquote (,) and unquote-splicing (,@) operators, as well
as rest parameters (&)."

Take it one step further and make quasiquoting be able to build arrays at
runtime. This is something that Daniel Gackle (gruseom) suggested for
Parenscript and I need to implement. I actually want to take it a step further
still and have a matching system like fare-matcher
(<http://www.cliki.net/fare-matcher>) based around quasi-quoting. If you write
all your code this way, you can go a long way towards eliminating dependence
on the specific data structures you use for representing s-expressions. Right
now I suspect you can go _all_ the way, and that it's a viable strategy for
making Parenscript self-hosting without adding runtime support for car/cdr to
JavaScript.

Speaking of runtime libraries, I think it's an implicit assumption you made
already, but if you want to be like Coffeescript or Parenscript, you obviously
need to avoid those.

