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.
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
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!"))
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)))
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'));
CLiki pages can be edited by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively