Monday, October 18, 2010

Lesson 9 - Multimethods and routing for our server

In Lesson 6 we saw how to parse a HTTP Request. In Lesson 7 we learned to send a proper response. For our server to really do something it needs to handle varied requests. So in this lesson we will learn to respond to requests for the index page ("/"), an about page ("/about"), a contact page ("/contact") and send a Not Found error for any other request.

To achieve the above we need to route the requests to the appropriate handler functions or otherwise called controllers. In traditional object oriented programming you would have created a base "Controller" class and created extensions to handle each type of request. In a functional programming language like Clojure we can achieve the same results using multimethods.

First let us write a route map for our program.

(def routes {
  "/" "index",
  "/about" "about",
  "/contact" "contact"
})
Here we define a map called routes. Each key in the map is the request path we want to handle, which is mapped to a corresponding string.

Next we define a multimethod with defmulti and name it controller.

(defmulti controller
  (fn [request]
    (routes (request :path))))
defmulti takes a function as its argument and based on the value returned it will call the appropriate function. In our case we pass it an anonymous function which takes a request object as its argument. The request object is the same parsed request we saw in Lesson 6. First we extract the path from the request (request :path). Then using the path as key in our routes map we return the corresponding string for the path.

Now how will it call the appropriate function? Here is our function to handle the index page.

(defmethod controller "index" [request]
  (send-response
        (assoc html-response :body "This is the index page")))
We have to define each of our required functions with defmethod. It takes the same name as the defmulti, but the next argument after the name is the dispatch-val. For this particular function to be called the function in defmulti must return a value equal to this dispatch-val. In our case the dispatch val is the string "index" which corresponds to our "/" path. The rest of the definition is the same as any other function you define.

Here are our other handler functions.
(defmethod controller "about" [request]
  (send-response
        (assoc html-response :body "This is the about page")))

(defmethod controller "contact" [request]
  (send-response
        (assoc html-response :body "This is the contact page")))
  
(defmethod controller :default [request]
  (println "HTTP/1.0 404 Not Found"))
Notice the last one. A dispatch-val of :default indicates that this is the default function in case no suitable function was found.

Here is the complete source code.
(use 'clojure.contrib.server-socket)
(use '[clojure.string :only (join split)])
(import  '(java.io BufferedReader InputStreamReader PrintWriter))

(def routes {
  "/" "index",
  "/about" "about",
  "/contact" "contact"
})

(defn parse-request []
  (loop
    [ result
        (zipmap
          [:method :path :protocol]
          (split (read-line) #"\s"))
      line (read-line)]
    (if (empty? line)
      result
      (recur
        (assoc
          result
          (keyword (first (split line #":\s+")))
          (last (split line #":\s+")))
        (read-line)))))
         
(def html-response
  { :status-line "HTTP/1.0 200 OK",
    :headers {:Content-Type "text/html"}})
            
(defn send-response [response]
  (let [headers (assoc (response :headers)
                  :Content-Length (count (response :body)))]
    (println (response :status-line))
    (println
      (join
        (for [key (keys headers) :let [value (headers key)]]
          (format "%s: %s\n" (name key) value))))
    (print (response :body))))

(defmulti controller
  (fn [request]
    (routes (request :path))))

(defmethod controller "index" [request]
  (send-response
        (assoc html-response :body "This is the index page")))

(defmethod controller "about" [request]
  (send-response
        (assoc html-response :body "This is the about page")))

(defmethod controller "contact" [request]
  (send-response
        (assoc html-response :body "This is the contact page")))
  
(defmethod controller :default [request]
  (println "HTTP/1.0 404 Not Found"))

(create-server
  8080
  (fn [in out]
    (binding
      [ *in* (BufferedReader. (InputStreamReader. in))
        *out* (PrintWriter. out)]
      (controller (parse-request))
      (flush))))



Tuesday, October 5, 2010

Lesson 8 - Macros! Macros! Everywhere

We saw anonymous functions in Lesson 4. Here is a simple anonymous function.
(fn [name] (println "Hello" name))
The problem with the above for me, coming after using javascript is that "fn" is too cryptic for my liking. I would have liked to have done
(function [name] (println "Hello" name))
I would rather write "function", which is more verbose, but makes code easier to understand for people coming to clojure from other languages. Now if I had some way, to kind of magically transform the code in the second line using "function" form to the first line using "fn", before the code is actually executed, I could have my way!

That is exactly what the defmacro function does. It takes a macro definition in the code, and replaces it with executable code at compile time. Macro's are like templates. It so happens that they generate executable code! This is what makes Macro's in Clojure and other dialects of Lisp so powerful, and so different to Macro's in other languages. Here is our simple trivial macro.

(defmacro function [args & body]
  `(fn ~args ~@body))

The defmacro function takes a name, "function" in this case and set of arguments. In this case the first argument is args, and the second "& body" represents a variable number of arguments. The backtick "`" indicates that "(fn ~args ~@body)" should not be evaluated. Instead what happens is that at compile time new code is generated to replace the macro with the "~" tilde arguments replaced with the actual arguments. So when the compiler encounters
(function [name] (println "Hello" name))
it replaces "~args" with "[name]" and "~@body" with whatever the rest of the arguments are, in this case "(println "Hello" name)". So the new code replacing the whole macro will be
(fn [name] (println "Hello" name))
which is what we wanted to begin with.

It is important to note that the arguments you passed to defmacro "[name] (println "Hello" name)" are not evaluated and are passed verbatim to the template. If you had passed the same arguments to a function on the other hand they would have been evaluated immediately.

Now let us actually execute some code.


(defmacro function [args & body]
  `(fn ~args ~@body))


((function [name]
  (println "Hello" name)) "World!")

What we are doing above is creating an anonymous function using the "function" macro and calling that function immediately with argument "World!". To execute an anonymous function immediately simply pass the function as the first argument of a list with the rest of the arguments in the list being the arguments to be passed to the function. The point to note above is that the variable "name" passed to the anonymous function is only visible within the function body .ie. it is a lexically scoped variable, just like the variables created with the "let" function we saw in lesson 7.

(defmacro function [args & body]
  `(fn ~args ~@body))

(println (macroexpand-1 '(function [name]
  (println "Hello" name))))

Run the program above. The macroexpand-1 function takes a quoted' macro and expands it to its executable form. In this case the expansion is
(clojure.core/fn [name] (println Hello name))

The defn function which we used for defining functions is also a macro. Here is a simple defn macro using our own "function" macro.

(defmacro function [args & body]
  `(fn ~args ~@body))
  
(defmacro define-function [fn-name args & body]
  `(def ~fn-name (function ~args ~@body)))

(define-function say-hello [name]
  (println "Hello" name))

(say-hello "World!")

Run the above code. So we created two macro's. "function" works exactly like "fn". And "define-function" works exactly like "defn".

Next we take on Multimethods and routing for our server.

Friday, October 1, 2010

Lesson 7 - What? No Variables in Clojure?

If you noticed by now, we have gone through six lessons and haven't yet created a variable. At least not explicitly. We did create some lexically scoped variables that were created implicitly as function arguments, or bindings to the "loop" and "reduce" functions we saw. It is highly unlikely that you would go through six lessons in any programming language (non Lisp) and not create a variable.

There are global variables in Clojure, which you create with the "def" function. And the "let" function which allows you to create lexically scoped variables. However the fact that we haven't used them so far is a good thing. The more variables you have in your program, the more you have to keep track of them. And the more the chances of inadvertently introducing a bug.

Also in Clojure you can get away with not creating variables because of the usage of higher order functions. Higher order functions are functions that take other functions as arguments. You already saw the higher order function "reduce" in action.

Clojure variables are immutable. You cannot modify the values after creation. You can rebind a variable name to another value though. This is a feature of Clojure not a bug! There are good reasons for this, concurrency being one of them.

Let us get on with improving our web server.



(use 'clojure.contrib.server-socket)
(use '[clojure.string :only (join)])
(import  '(java.io BufferedReader InputStreamReader PrintWriter))
    
(def default-response
  { :status-line "HTTP/1.0 200 OK",
    :headers {:Content-Type "text/html"},
    :body "<h1>A Web Server written in Clojure</h1>"})
  
(defn send-response [response]
  (let [headers (assoc (response :headers)
                  :Content-Length (count (response :body)))]
    (println (response :status-line))
    (println
      (join
        (for [header (keys headers)]
          (format "%s: %s\n" (name header) (headers header)))))
    (print (response :body))))

(create-server
  8080
  (fn [in out]
    (binding

      [ *in* (BufferedReader. (InputStreamReader. in))
        *out* (PrintWriter. out)]
      (send-response default-response)
      (flush))))



Run the above code and browse to localhost:8080. What we have done here is set up a default response for our web server. We define a global variable default-response, which is a map structure with three keys and their corresponding values. Note that the value of the ":headers" key is itself another map.

We then define the send-response function which writes to the output stream, a given response map. The first thing the function does is define a lexically scoped variable called headers.


  (let [headers (assoc (response :headers)
                  :Content-Length (count (response :body)))]


The let function takes an array of bindings. Here there is only one binding. The variable "headers" is bound to the return value of the assoc function. The assoc function takes the :headers map of the response and adds a :Content-Length key, and its value, is returned by the count function which returns the length of the :body value of the response. The visibility of this bound "headers" variable is the body of the "let" function. Their are three "println" statements in this function. Let us look at the second one.

(join
  (for [header (keys headers)]
    (format "%s: %s\n" (name header) (headers header)))))

Let us look at the "for" statement first. It takes the collection of keys returned by (keys headers) and binds each value to the "header" variable and executes the "format" function for each header key. The format string takes two parameters, a string value of the header key (name header) and the value of the header (headers header) and replaces these two values in the format string. The collection of header lines returned by "for" is then joined by the "join" function.

In this case the output of the function will be

HTTP/1.0 200 OK
Content-Type: text/html
Content-Length: 40


<h1>A Web Server written in Clojure</h1>


We tackle Macro's in the next Lesson.