Search This Blog


Wednesday, March 23, 2011

Hello Web : Dynamic Compojure Web Development at the REPL

;; In an article which convinced many people to have a look at LISP


;; Paul Graham wrote about how nice it was to write a web application from an
;; environment where everything was controllable from the REPL.

;; Here's how to create a web server at the REPL

;; First we'll need a function to generate a simple web page
(defn yo [] "Hello")

;; Now we attach that to /
(use 'compojure.core)
(defroutes main-routes (GET "/" [] (yo)))

;; We should wrap our routing table in a handler (note the #')
(use 'compojure.handler)
(def app (site #'main-routes))

;; And then we can run it with jetty (again note the #')
(use 'ring.adapter.jetty)
(def server (run-jetty #'app {:port 8080 :join? false}))

;; So now we're serving web pages. Go look at http://localhost:8080/

;; The web browser of the gods:
;; $ watch -d -n 1 curl -sv http://localhost:8080/ 

;; We can stop the server
(.stop server)

;; And restart it:
(.start server)

;; We can redefine those functions, and the change takes effect immediately:
(defn yo [] "<h1>Hello<h1/>")

;; (you will need to refresh the page, unless you use twbotg)

;; HTML is a bit of a pain. We already have a syntax for tree-structured data:

(use '

(defn yo []      
  (html5 [:head
          [:title "Hello World"]]
          [:h1 "Lisp is the future"]]))


;; I'm confused: see

;; The reason for the #' above is to make sure that we can redefine main-routes
;; and app at runtime. Without the indirection, the changes don't seem to get picked up.

;; And yet they do get picked up when you redefine yo.

;; I do not understand what is going on here. Any explanation would be welcome!

To start a REPL with the necessary libraries available, you can use maven.

To install maven on ubuntu:
$ sudo apt-get install maven2

Put this xml in a file named pom.xml, and then type
$ mvn clojure:repl
in the same directory. For a swank server that you can use with emacs, use
$ mvn clojure:swank

The first time you run it it will do an incredible amount of downloading to get all the necessary bits, but after that it's fairly speedy.

  <description>maven, clojure, emacs, swank, compojure: together at last</description>

  <!-- repositories -->

  <!-- libraries -->

      <!-- talios' clojure-maven-plugin provides mvn clojure:swank etc -->
      <!-- The versions plugin allows you to find out what you can upgrade -->
      <!-- mvn versions:help -->
      <!-- mvn versions:display-dependency-updates -->



  1. #'app is short for (var app). For example:

    (def foo 10)

    (type (var foo))
    (type #'foo)

    Passing a var (instead of the value) transparently re-reads the value inside the var in `run-jetty`, which leads to the dynamic magic. I blogged about it here:


  2. twbotg = the web browser of the gods =

    $ watch -d -n 1 curl -sv http://localhost:8080/

  3. As a web developer, I learn important things about REPL. The coding which shared in this post is also very helpful to understand this process. custom web apps development service