At the start of the year I announced that I was setting myself the challenge of learning 12 new programming languages during 2010. That works out at a language a month, so seeing as it's almost the end of March you might expect me to be wrapping up my third language. Unfortunately that isn't the case. I'm just about to move on to my second. I'm still optimistic I can achieve the target of 12 new languages this year though, so expect future updates to be more regular.

Getting Starting with Clojure

Getting up and running with Clojure was made easy to to the wealth of documentation. There's a great getting started guide, and a guide specifically for Clojure on Ubuntu. In terms of programming environment there's a round up of Clojure IDEs. I stuck to Vim, but I didn't take it as far as this guide, which describes turning Vim into a fairly comprehensive Clojure IDE.

Clojure has a REPL, which I always find makes learning a new language easier. When you want to find something out just type it in a see what the result is! The default REPL doesn't support arrow navigation or pressing up to run previous commands though, so it can be a little frustrating. There are guides on enhacing the REPL with this functionality.

One of the first things I did was put together the following shell script which either runs the specified Clojure script, or gives you a REPL if no script was specified.

#!/bin/sh CLOJURE_JAR = "/opt/clojure-1.1.0/clojure.jar" if [ -z "$1" ] ; then java -jar "${CLOJURE_JAR}" else java -jar "${CLOJURE_JAR}" "$@" fi

Writing Code

I decided to write a port scanner in Clojure, which would introduce me to command line argument handling, the network API, and parallelization.

One of the great things about Clojure is that despite being such a new language there is so much example code available on the web. I was able to find a Clojure network scanner by Travis Whitton which detailed all of the network related code I'd need to my port scanner. Travis uses Clojure's agents for parallelization, which is something else I borrowed from his script. I was amazed at how easy it was to parallelize the lookups. So much simpler than threading. If there is only one thing I take away from Clojure it'll be its interesting approaches to parallelization.

So without further ado here is my Clojure port scanner:

( import ' ( java.io IOException ) ' ( java.net Socket ) ' ( java.net InetSocketAddress ) ' ( java.net SocketTimeoutException ) ' ( java.net UnknownHostException )) ( if ( == ( count *command-line-args* ) 1 ) ( def hostname ( first *command-line-args* )) ( ( println "Usage: scanner <hostname>" ) ( System/exit 1 ) )) ( defn port-open? [ hostname port timeout ] ( let [ sock-addr ( InetSocketAddress. hostname port )] ( try ( with-open [ sock ( Socket. )] ( . sock connect sock-addr timeout ) port ) ( catch IOException e false ) ( catch SocketTimeoutException e false ) ( catch UnknownHostException e false )))) <li> ( defn host-port-open? [ port ] ( port-open? hostname port 5000 )) ( def port-list ( range 1 1024 )) ( def agents ( for [ port port-list ] ( agent port ))) ( println ( str "Scanning " hostname "..." )) ( doseq [ agent agents ] ( send-off agent host-port-open? )) ( apply await agents ) ( doseq [ port ( filter deref agents )] ( println ( str @ port " is open" ))) ( shutdown-agents )

I'm sure it is far from an idiomatic solution, so any suggestions for improvement are welcome. Running the scanner with my bash script gives the following output:

$ ./clj.sh scanner Usage: scanner <hostname> $ ./clj.sh scanner github.com Scanning github.com... 22 is open 80 is open 443 is open

What's next?

I've barely scratched the surface of Clojure, but I've certainly become more aware of some of the concepts and idioms used by the language, which is what I was hoping for. I'll be looking for more projects in the future where I can make use of it. For now though I need to move on to another language as part of my challenge. As I'm running behind I plan to go with one that I don't think will be too unfamiliar, either Go or Fantom. I'll keep you posted!