Friday, September 24, 2010

Lesson 2 - Clojure Functions

In Lesson 1 we saw two Clojure functions in action. Spit and eval. Clojure comes with a set of functions available to you by default. These functions reside in the "clojure.core" library. Have a quick look at the available functions in clojure.core at the link given below and come back here.
https://clojure.github.io/clojure/clojure.core-api.html#clojure.core

Did you notice "+" "-" "/" "*" among the available functions? Yep! These are functions in Clojure. Try the following function in the clojure REPL. (To run the REPL just run clojure without any arguments).
(+ 1 2)

In Clojure every thing you do is with functions and functions only. In addition to the Clojure core functions you have the clojure.contrib library, with additional functions contributed by a bunch of nice people. Then you have third party libraries for doing various stuff. And thats not all. You have the whole load of java functions available to you in Clojure.

We're going to output some XML to stdout using emit.

Type (emit "hello") in a file called helloxmlfile.clj and run it using lein exec.
//helloxmlfile.clj
(emit "hello")
and
$ lein exec helloxmlfile.clj
BOOM! Wall of errors.

Scroll up through the wall and you'll see some English that might make some sense.
"Unable to resolve symbol: emit in this context"

What this means is that the Clojure compiler couldn't understand what or who you were referring to when you said 'emit'.

The emit function is part of the namespace clojure.xml, which is not imported in the lein REPL. You could use spit and println in the previous examples because they're part of the clojure.core namespace, which is imported when the lein REPL starts up. (Fun exercise: Type (all-ns) in the lein REPL to see all the available namespaces. Don't bother if the output doesn't make any sense now.)

So, we're going to tell our Clojure compiler where to look for, when we say emit, by using 'use'.
// helloxmlfile.clj
(use 'clojure.xml)
(emit "hello")
and
$ lein exec helloxml.clj

<?xml version='1.0' encoding='UTF-8'?>
hello
No error wall! Sweet. But, that's not enough XML.

Make your helloxmlfile.clj look like this and run lein exec again.
///helloxmlfile.clj
(use 'clojure.xml)
(emit {:tag :parent :attrs {:value "Hello"}
:content[
{:tag :child :attrs {:value "World"}}
{:tag :child :attrs {:value "Clojure"}}
]})
and
$ lein exec helloxml.clj

<?xml version='1.0' encoding='UTF-8'?>
<parent value='Hello'>
<child value='World'/>
<child value='Clojure'/>
</parent>
The parent and child elements come from a map, a data structure in Clojure, with alternating keys (:keys) and values. A key can be a value. A map can also be a value. The content is a vector that contains a map. We'll delve into keywords and vectors and maps later.

What we have now is some XML that is wasted on stdout. Let's save that to a file using what we've learnt earlier.

Combine spit and with-out-str to make your program look like this.
///helloxmlfile.clj
(use 'clojure.xml)
(spit "hello.xml" (with-out-str (emit {:tag :parent :attrs {:value "Hello"}
:content[
{:tag :child :attrs {:value "World"}}
{:tag :child :attrs {:value "Clojure"}}
]})))
And run it with lein exec.
$ lein exec helloxml.clj
There's no output on the screen. That's excellent because if you've been paying attention, we were outputting XML to file.

Check the contents of hello.xml.
$ cat hello.xml
<?xml version='1.0' encoding='UTF-8'?>
<parent value='Hello'>
<child value='World'/>
<child value='Clojure'/>
</parent>
Well done.

Alright! To sum it up, we just learnt to send the output of our Clojure program to file. Writing XML. A bit about namespaces. Using 'use'.

Lets roll out our own Clojure function in the next lesson.

1 comment:

  1. This is cool, you get straight to some useful code right away, without making us feel overwhelmed.

    ReplyDelete