[gambit-list] compiling javascript syntax to scheme

Per Eckerdal per.eckerdal at gmail.com
Mon Mar 22 20:06:42 EDT 2010


Hi,

Just to shed light on some of the potential pitfalls that I see could arise in this kind of project:

22 mar 2010 kl. 23.11 Ben Weaver wrote:

> What I've been thinking of is basically a parser for a nearly-JS syntax that generated s-expressions targeting some lightweight macro layer.  Where JS and Scheme semantics or runtime environments diverged, it'd be fine to use Scheme's.  Instead of implementing a JS interpreter, it'd be a JS-like syntax for the Scheme interpreter.
> 
> For example:
> 
> function adder(n) {
>     return function(a) {
>         return n + a;
>     }
> }
> 
> Could be parsed into something like:
> 
> (function adder (n)
>   (function #f (a)
>     (+ n a)))

I don't think it's trivial to parse like that if you want return statements to behave like they do in C-like languages. You'd have to parse to

(function adder (n)
  (return
    (function #f (a)
      (return (+ n a))))

which would compile into something like

(define adder
  (lambda (n)
    (call/cc
      (lambda (return)
         (return
            (lambda (a)
              (call/cc
                (lambda (return)
                  (return (+ n a))))))))))

Otherwise return wouldn't be able to exit in the middle of a function call, which would destroy the purpose of having the keyword at all, and only confuse people who expect it to work like it does in C. Of course, it's possible to compile away most call/cc invocations, but that's not completely trivial to do.
  

> And with the aid of something like:
> 
> (define-macro (function name params . body)
>   (if name
>       `(define ,name (function #f ,params , at body))
>       `(lambda ,params , at body)))
> 
> Get something like:
> 
> (define adder
>   (lambda (n)
>     (lambda (a)
>       (+ n a ))))

I would strongly recommend to be careful with expanding to define forms. IME define forms are one of the more fragile parts of Scheme, and when you push it to its limits it doesn't really behave like you'd expect it to. Something as simple as

function() {
  function a() { return "a"; }
  document.write(a());
  function b() { return "b"; }
  return b();
}

wouldn't work, because the definition of b is after the document.write expression. The kind of code in the previous example, where you mix function definitions and expressions, is very common in Javascript and is crucial to be able to write good-looking and idiomatic code. I would consider expanding it to let forms instead, as in

(lambda ()
  (call/cc
    (lambda (return)
      (let ((a (lambda () (call/cc (lambda (return) (return "a"))))))
        (document.write (a))
          (let ((b (lambda () (call/cc (lambda (return) (return "b"))))))
            (return (b))))))))

This would result in different scoping rules than Javascript, though, since this is valid Javascript:

function () {
  a = 5; // Set a to be 5
  var a;  // Define that a is a local variable. It doesn't matter where this declaration is, as long as it is inside of this scope (in this case the function)
  return a;
}

(You can of course argue that these scoping rules of Javascript are a mistake and/or historical cruft) Overall, I would be really careful in the design phase of this piece of software, and I would not expect the code transformation to be trivial unless you can live with having a very leaky abstraction. But that would ruin most of the point of convincing newbies to write Scheme.

Also, I would study the implications of the differences between the basic data structure of Scheme and Javascript, and their implications for writing everyday code. Scheme's core type is the cons cell. Javascript's core datatype is the hashtable. This does not only have implications to how you should try to write a compiler that generates efficient code. I believe that Javascript's syntax is designed to be good at manipulating hash tables, while Scheme's syntax is designed to be good at manipulating lists. Even if you'd write a good Javascript to Scheme interpreter/compiler, it would still be difficult to integrate good Javascript code with good Lisp, because idiomatic and beautiful Javascript uses hash tables in a way that would be clunky and stupid to do in Scheme, just like good Scheme code uses lists in a way that would be stupid to do in JS.

It is true that Javascript's semantics are very similar to Scheme's, but the fact that Javascript primarily manipulates hash tables combined with its syntax makes Javascript code surprisingly different to Scheme code doing the same thing. The problem is, as soon as you move away from having lists as a core data structure you will also go away from recursion as a such natural tool and many of the other characteristics of Lisp. But if you stay with lists as a core data structure, a C-like syntax will be horribly clunky.

I hope that this will help you to avoid some of the mistakes that I have made in related projects.

Good luck!

/Per




More information about the Gambit-list mailing list