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. However, there is the "lexical-let" macro which does.
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