Saturday, September 25, 2010

Lesson 3 - User defined functions in Clojure

In this lesson we will roll out our own function in Clojure.

We'll use spit and with-out-str to write to file. But instead of emit, we'll use the emit-element function for our tags.

Type out the following code and save it as hellohtml.clj.
//hellohtml.clj

(use 'clojure.xml)
(println
 (with-out-str
  (emit-element
   {:tag :html :content
    [{:tag :body :content
     [{:tag :h1 :attrs {:style "color:red"} :content["My Homepage"]}
      {:tag :div :attrs {:style "text-align:center"} :content["This is the content"]} ]}]})))
Run it using lein exec
$ lein exec hellohtml.clj
<html>
<body>
<h1 style='color:red'>
My Homepage
</h1>
<div style='text-align:center'>
This is the content
</div>
</body>
</html>
If you compare it to the helloxmlfile.clj from Lesson 2, you should have an idea of what the above code does.

I know what you're thinking. SO MANY BRACKETS! Relax, you'll be fine.

What do we have here? HTML wasted on stdout. Let's save that to file by replacing println with spit <filename>.
//hellohtml.clj
(use 'clojure.xml)
(spit "hellohtml.html"
 (with-out-str
  (emit-element
   {:tag :html :content
    [{:tag :body :content
     [{:tag :h1 :attrs {:style "color:red"} :content["My Homepage"]}
      {:tag :div :attrs {:style "text-align:center"} :content["This is the content"]} ]}]})))
Run it using lein exec.

As expected we have a file called hellohtml.html with the code that was earlier sent to stdout.

Now let's generate more pages for our fictional website. We're going to be consistent with our minimalist design and the only parts that will change are the filename, title and the content. Since that's the case, why repeat the code for each page when you can roll your own function to do exactly that.

Let's create a new file called hellofunchtml.clj and modify our exisiting code so it looks like this.
//hellofunchtml.clj
(use 'clojure.xml)
(defn generate-page [filename title content]
  (spit filename
   (with-out-str
     (emit-element
       {:tag :html :content
        [{:tag :body :content
         [{:tag :h1 :attrs {:style "color:red"} :content[title]}
          {:tag :div :attrs {:style "text-align:center"} :content[content]} ]}]}))))

(generate-page "about.html" "About Me" "There's nothing to say.")
(generate-page "contact.html" "Contact Me" "Here's my email and phone. Spam me.")
(generate-page "faq.html" "FAQ" "Why was six afraid of seven? It wasn't. Numbers are not sentient and thus incapable of feeling fear.")
(generate-page "index.html" "Index" "Welcome to my homepage")
Run it using lein exec
Congratulations! You define functions in Clojure using defn. defn takes a function name, a set of params in a vector, and a function body as its arguments. defn also takes two more optional arguments doc-string? and attr-map?, which we will cover in another lesson. In the last lines we called the generate-page function passing it the three required parameters.

1 comment:

  1. These are good lessons in general, but they would benefit (a lot, IMO) from some syntax highlighting. Especially for someone new to Closer / Lisp dialects, with all the nesting brackets etc., a bit of colour would help understanding things quicker

    ReplyDelete