Ralph – Future

about 2 minutes

The last refactoring of Ralph’s compiler began almost two years ago. After starting with great ideas and ambition, my Master’s thesis interrupted the development just a month later. After graduating I got a job which did not leave much time for large side-projects either. In spring I finally spent a few days finishing off some of the important features.

The compiler now properly performs syntax-quoting at read-time, rather than compile-time, and handles nesting. This fix finally allows writing macro-writing macros, like once-only.

Ralph also gained the non-local-exit macro block (called bind-exit in the old standard). I was always afraid of adding it, knowing that functions containing a try-catch block would not be optimized in V8. However, it turns out the protected expressions stay optimized if they are moved into a function outside of the try-catch block, and the protected expression is an invocation of that lifted method.

The alpha-conversion pass got refactored into CPS, similar to the A-Normal Form pass. This allows for example to prepend code like module loading statements for free variables that are imported.

To make it easier to debug the compiler, I improved the readability of the generated JavaScript by replacing the naive JavaScript code generator with one that is emitting Mozilla’s Parser API AST and using escodegen. There are now also pretty-printing facilitie which can assist understanding the output of transformation passes. After reading Philip Wadler’s “A prettier printer”, I implemented a strict variant as described by Christian Lindig in “Strictly Pretty”. However, it was rather cumbersome to define printing functions with the desired line wrapping and indentation behaviour for S-expressions, so I ported Marc Feeley’s genwrite.scm. It is less generic, but much more suited for Ralph.

Last but not least, Ralph finally has a REPL. The interactor compiles Ralph expressions entered by the user to JavaScript, sends the generated code to an evaluator via the WebSocket protocol, and prints the response. Currently there are evaluators for node.js and browsers.

$ NODE_PATH=core node repl.js
ralph/core> (map (curry +) [1 2 3])
(1 2 3)
ralph/core> (ralph/compiler/reader::read
        ...   (make ralph/stream::<string-stream>
        ...         string: " ( test 23 test: \"42\" )"))
(test 23 test: "42")
$ NODE_PATH=core node repl.js --remote
ralph/core> (get (%native "navigator") "userAgent")
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.66 Safari/537.36"

I always hoped to use Ralph for future projects once it is in a usable state. The compiler and the language are more or less stable, but there are still some features missing that would make developing real-world applications much easier, like better IDE integration and source map support. Given how long the last refactoring took, I think it is time for me to declare Ralph as “done” and move on to new projects. If you have questions, are interested in using it, or even would like to continue its development, please let me know!