ParenscriptTipsAndTricks

This page is for snippets of code that are useful in writing ParenScript code. Please add your own!

Debugging with FireBug

The FireBug extension for FireFox has a nice console that you can write output to. This is much nicer than using "alert", since you can write as much debugging output as you want without annoying the user (or yourself). Here's a debug macro that outputs a message only if the *debug* variable is true:

(defvar *debug* t)

(defjsmacro debug (message) (if *debug* `(if window.console (console.log ,message))))

When you're ready to deploy your code, set *debug* to nil, and all the "console.log" calls will disappear from your JavaScript output.

Returning multiple values

These macros provide a way to return multiple values from a function. The "receive" macro is also useful in asynchronous programming, since it uses a callback that can delay execution arbitrarily.

(defjsmacro receive (args proc &rest body)
  `(,proc (lambda (,@args) ,@body)))

(defjsmacro values (&rest args) `(lambda (proc) (proc ,@args)))

Example:

(defun divmod (a b)
  (return (values (/ a b) (% a b))))

(receive (div mod) (divmod 10 3) (debug (+ "div: " div ", mod: " mod)))

Result:

div: 3.3333333333333335, mod: 1

"After" macro

Lisp makes it easy to wrap code in macros that would otherwise requre passing functional arguments. Here's a macro that executes some code in the future:

(defjsmacro after (ival &rest body)
  `(set-timeout (lambda () ,@body) ,ival))

Example:

(after 2000
  (alert "two seconds have elapsed!"))

Real let-bindings

The "let" macro that comes with ParenScript doesn't introduce a new lexical scope like proper Lisp. This means you can write idiomatic Lisp without losing efficiency on the JavaScript side, but sometimes you really do need the scoping behavior of a proper "let". Here's a replacement that gives you a nested scope by creating and invoking a lambda. This is copied almost verbatim from On Lisp by Paul Graham (he calls it "our-let" in his example).

(defjsmacro real-let (binds &body body)
  `((lambda ,(mapcar #'(lambda (x)
                         (if (consp x) (car x) x))
                     binds)
      ,@body)
    ,@(mapcar #'(lambda (x)
                  (if (consp x) (cadr x) nil))
              binds)))

Binding Elements by IDs

You can use the above technique to implement other binding macros, such as the following, which looks DOM elements up by their IDs:

(defjsmacro with-elements-by-ids (binds &body body)
  `((lambda ,(mapcar #'(lambda (x)
                         (if (consp x) (car x) x))
                     binds)
      ,@body)
    ,@(mapcar #'(lambda (x) `(document.get-element-by-id
                              ,(if (consp x)
                                   (cadr x)
                                 (string-downcase (symbol-name x)))))
              binds)))

Example:

(with-elements-by-ids (mydiv (other "otherdiv"))
  (setf mydiv.inner-h-t-m-l other.inner-h-t-m-l))

Result:

(function (mydiv, other) {
   mydiv.innerHTML = other.innerHTML;
 })
(document.getElementById('mydiv'), document.getElementById('otherdiv'));


ParenScript

CLiki pages can be edited by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively