AsciiMathML

Showing posts with label clojure. Show all posts
Showing posts with label clojure. Show all posts

Wednesday, April 14, 2010

Writing JQuery code with Scriptjure

I'm the author of Scriptjure, a Clojure library / DSL for generating javascript from clojure code. I mainly developed it for embedding glue javascript in HTML pages. Let's take it out for a spin.

I'm not going to go over the basic rules. See the above link for more detail. Since 'everyone' uses jquery these days, let's make a simple example, setting up an on-click handler that pops up an alert box. Pretty much the simplest interesting thing you can do.


<img id="kitty-img" src="kitty.jpg">
<script type="text/javascript"
$(document).ready(
function() {
$("#kitty-img").click( function() {
alert("you're a kitty!"); })}) />

Not very pretty.

Let's naively translate that jquery code into compojure and scriptjure:

(html
[:img#kitty-img { :src "kitty.jpg"}]
[:script {:type "text/javascript"}
(scriptjure/js (.ready ($ document)
(fn [] (.click ($ "#kitty-img")
(fn [e] (alert "you're a kitty"))))))])

Not much of an improvement in terms of length. Of course, we've gained a lot because now there's no string interpolation to generate javascript, but we can do better. Let's do some refactoring. We're going to define several clojure functions that output javascript.

(defn on-ready
"Runs a chunk of javascript when the document is ready"
[js]
(scriptjure/js* (.ready ($ document)
(fn [] (clj js)))))

(defn on-click
"installs js as an on-click handler"
[selector js]
(scriptjure/js* (.click ($ (clj selector))
(fn [e] (clj js)))))


Using our two shiny new functions, the example looks like:

(html
[:img#kitty-img {:src="kitty.jpg"}]
[:script {:type "text/javascript"}
(scriptjure/js (on-ready (on-click (js*
(alert "you're a kitty!")))))])


Why did I use js*? Remember that (js) returns a string, a "compiled" chunk of javascript. Assume we didn't use js*: on-click would return a string. On-ready would treat the output of on-click as a javascript string rather than javascript code, and return


$(document).ready( function() { "$(\"kitty-img")..."}

Rather than

$(document).ready( function() { $("kitty-img")...}

Note the quotes.

So when writing clojure code that generates javascript, typically you'll want to use js*, and then make sure the final recipient calls (js) rather than (js*). But you know, remembering when to call (js) is annoying. Let's fix that.

(defn script
"wraps js code in an HTML script tag"
[js]
(html
[:script {:type "text/javascript"}
(scriptjure/js (clj js))]))

Now our example looks like

(html
[:img#kitty-img {:src "kitty.jpg"}]
(script (on-ready (on-click "#kitty-img"
(js* (alert "you're a kitty!"))))))

Much better! This technique makes generating javascript significantly less painful. You can the same approach to simplify e.g. Ajax updating pages. Maybe I'll get around to adding all of these little helper functions to scriptjure. If you find any bugs or have any suggestions for how to improve scriptjure, I'd love to hear them.

Wednesday, July 08, 2009

Can your programming language do this?

In information theory, entropy is the measurement of the amount of randomness in a probability distribution.

It's defined as



In my code, it's

(defn entropy [X]
(* -1 (Σ [i X] (* (p i) (log (p i))))))


Compare the similarity of my code above with the actual definition. Having a language that lets you declare constructs that look very similar to their mathematical definition is a huge win for readability.

The two important features I used in this example are

1) Clojure allows identifiers to be unicode
2) Macros

My macro is defined as

(defmacro Σ
"computes the sum of a series."
[[var-name values] & body]
`(reduce +
(map (fn [~var-name]
~@body) ~values)))


Of course, I have a similar one:

(defmacro Π
"computes the product of a series."

You can imagine how it looks.


The other big win here is I've abstracted out *how* the sum and product of a series are implemented. This allows me to do performance optimizations behind the scenes. For example, I could replace that call to map with a call to pmap.

The point of this isn't to show off how Clojure is better than other languages; I'm trying to show that these features are powerful. If your language of choice doesn't have these features, maybe it should.

Of course, macros are just a means to an end. I find them great, but that doesn't mean they're the One True Way to accomplish our goal of readable code. I'd love to see examples of solving this problem in other ways, in any language.

Wednesday, June 17, 2009

Preventing tests (or, 5 Whys in action)

I was working on some Clojure for my site, Reasonr today. I develop on my laptop, using Emacs, and I have a production box on AWS. I recently set up swank-clojure on the production web server, so I can SSH tunnel onto the box, and get a REPL. Very cool.

There is one downside however. I had tunneled into the box to check something out, and then forgot about. I went back to developing. I was ready to test my changes, and I ran (run-all-tests). Normally this would be fine, except my slime buffer was still connected to the production box, and run-all-tests does more than just unit tests, it does things like insert (fake) data into the database and makes sure it comes back out correctly. And I was still connected to the production box, so now that fake data is in my production DB.

#$^%#

After deleting all the crap data out of the DB, I thought of Eric Ries' 5 Whys, and set about trying to make sure this never happens again.

Step 1: Don't allow tests in production



(defn no-tests-for-you []
(println "You're in production, dumbass. No tests for you!"))

(defn prevent-tests []
(with-ns/with-ns 'clojure.contrib.test-is)
(def run-all-tests user/no-tests-for-you)
(def test-ns user/no-tests-for-you)
(def test-var user/no-tests-for-you))

(when (= winston.env/environment :production)
(prevent-tests))


Here, I have a dummy function that reminds the user we're talking to the production box. I also have a function that redefines most of the ways to run clojure tests. with-ns is from clojure.contrib.with-ns, which evaluates body in the specified namespace. Thankfully, (run-all-tests) is the most common way for me to start tests, and the remaining ways all take more effort to run, so this net will likely catch most of my goofs. I also have a var, winston.env/environment which specifies if we're in production or development. If we're in production, we obviously don't want to run tests.

Step 2: Fix the prompt



A big part of my confusion was because I wasn't sure which box I was connected to. Let's fix that by modifying the repl prompt to print the hostname. I already had code that started a repl manually, rather than using the default one in clojure.main. All I had to do was supply a new definition for the repl-prompt

(def hostname (.trim (sh "hostname")))

(defn repl-prompt []
(printf "%s:%s=> " winston.env/hostname *ns*))

(defn start-repl []
(binding [clojure.main/repl-prompt winston.repl/repl-prompt]
(clojure.main/repl)))



I used the sh function from clojure.contrib.shell-out to determine the hostname, and store that in a variable. Then I modified the repl prompt to print hostname:namespace=> rather than just "namespace=>". binding is clojure's way to safely monkey patch. Inside the scope of binding, all calls to clojure.main/repl-prompt will be replaced with calls to winston.repl/repl-prompt.

Step 3: Fix Slime?



I sort of cheated on step 2. Everything I wrote works great, but it doesn't fix Slime. Apparently slime hardcodes the definition of the prompt. You'll see the modified prompt when using the repl at the console, but not via slime. And I don't know of a good way to fix the slime prompt. Anyone out there have ideas?