Foognostic blogs Seeking knowledge of foo

26Jan/10Off

My week of Clojure and Emacs

This week has been very busy and useful for me with Clojure and Emacs.

Clojure

  • I continued to mature Pokerepl. The interfaces/defprotocols were reorganized into one file. Implementations/deftypes each have their own file.
  • Switched Leiningen branch from stable to master (think 1.1 alpha) and chopped out all those :gen-class warts
  • Lightly adopted @cemerick's very cool string interpolation macro -- it's the #{bar} in "foo #{bar}".
  • Got Nailgun working to speed up unit tests.
  • But @technomancy pointed out that I could use TONS less memory and have a better experience with clojure-test-mode. It runs instantly and highlights errors:

Image of a test failing

Emacs

  • I~ stopped~ backup~ files~ from~ appearing~ everywhere~ (thanks to ESK for clueing me in to backup-directory-alist)
  • Stopped avoiding color-theme finally. Using the rotor and late-night themes mostly, but will probably have to write my own at some point. It will be ugly, but such are my aesthetics.
  • Now using and happy with the Inconsolata typeface.
  • I installed but still shy away from paredit-mode. It's my own fault for not having watched this awesome preso yet.
Filed under: clojure No Comments
18Jan/10Off

Wrapping deftype factory methods

(Standard disclaimer: Clojure is a new hobby.)

I'm exploring the upcoming feature in Clojure called deftype. Similar to defining a class in Java, deftype is a way of grouping related data and methods. Actually, it's quite an improvement over the old (defstruct) way. defstruct only groups related data together.

As ever, code examples are based on my side project pokerepl. This entire post focuses on the suits and values in a deck of playing cards.

Here's the old way of doing things:

;; define the struct
(defstruct Card :value :suit)

;; instantiate it
(def card (struct Card :ace :spades))

Here's my first baby step down the new path:

(deftype Card [#^clojure.lang.Keyword value
               #^clojure.lang.Keyword suit]
  Object
  (toString []
            (str (get value-map value "?")
                 (get suit-map suit "?"))))

(def card (Card :ace :spades))

It's longer, but it does more. We get optional type hinting for the data (seen here as #^clojure.lang.Keyword). The type being extended is listed (Object in this case). The override of Object.toString means all instances will be pretty printed automatically.

What you don't see is a "constructor". There's no need because Clojure provides reasonable defaults here; the values for "value" and "suit" are passed to new instances of Card.

Unfortunately that wasn't good enough. Fortunately Clojure is a very dynamic programming language, so I could easily replace the implicitly defined factory function with my own code. To summarize, a call to (deftype Foo [bar]) generates two factory functions (Foo [bar]) and (Foo [bar meta-map ext-map]). It's possible to overwrite these but unclear how to honor the postconditions... exactly what gets returned?

So rather than replace the factory function entirely, I decided to embrace and extend it!

(def old-Card Card)

(defn Card
  ([two-chars]
     (apply Card (map (partial nth (seq two-chars)) [0 1])))

  ([value suit]
     (let [value-sym (if (= clojure.lang.Keyword (class value))
                       value
                       (get (zipmap (vals value-map) (keys value-map)) value))
           suit-sym (if (= clojure.lang.Keyword (class suit))
                      suit
                      (get (zipmap (vals suit-map) (keys suit-map)) suit))]
       (old-Card value-sym suit-sym))))

So that's a bit of a mess, but the requirements were too. I needed syntactical sugar to make dealing with this code palatable. And now I have said sugar. The following is now true:

(= (Card :ace :spades)
   (Card "AS")
   (Card \A \S))

My main gripe with (deftype) is that it complicates editor support. Previously, "defn" and a relatively small list of keywords meant a list was a function definition. Functions indent differently than other types of lists. deftype means the editor is going to have to understand the source better. But, it's a small and good problem to have IMO. Also hoping for destructuring support in vector for param arguments.

Filed under: clojure, code No Comments
12Jan/10Off

Presenting on Leiningen to Cap-Clug

I'm excited to present Leiningen to a Wash. DC metro area Clojure meetup, a.k.a. Cap-Clug. Leiningen is a Clojure-friendly build tool, and I've been very happy using it on Pokerepl.

In the presentation I am going to spend most of my time explaining how to build a minimal yet friendly jar file starting from scratch. Time permitting we will be able to cover more spiffy features like integration with Clojars.

The other scheduled presentation is going to be great; @Fogus is co-writing the next Clojure book and he will be presenting on features new in version 1.1.

If you are in the Washington DC metro area and have any sort of interest in Clojure, run don't walk to register -- the first meeting is Thurs Jan 14th, 2010. See you there!

Tagged as: 1 Comment
3Jan/10Off

Juggling versions of Clojure?

(Standard disclaimer: Clojure is a new hobby.)

EDIT: also try rake clojure:new:repl

I am looking forward to trying datatypes on Pokerepl. However, that means using the new branch of Clojure, and indirectly, more work on my part to clone/track/build/deploy the new branch.

Almost everyone would and should use Leiningen to experiment with the new branch. Simply update your project.clj something like this:

   :dependencies [[org.clojure/clojure "1.1.0-new-SNAPSHOT"]
                  [org.clojure/clojure-contrib "1.1.0-new-SNAPSHOT"]]

At the moment though, updating ~/.clojure was beyond the ken of any automated tool I knew of. So I wrote a Rakefile and gave it a cheesy name... juggling Clojure... hmm... howzabout Clojuggle? Well... yeah... sorry for that.

Back to the point: automating thorough (~/.m2 and ~/.clojure) updates and deployments of various versions of Clojure. Supporting common tasks (wipe, clone, build, &c.) for several branches of several repositories means a thousand tasks will bloom and make the tool hard to use.

Ruby and the Rake API make it easy to define or show tasks only when relevant. I really enjoyed writing this code. Anyways, typing rake brings you to this:

$ rake
Welcome! Try 'rake -T' for a list of useful tasks, or try
    rake clojure:master:all clojure-contrib:master:all

The default task provides some basic usage. Let's take a look at the initial set of tasks:

$ rake -T
rake clojure-contrib:master:all  # Do everything for master branch of clojure-contrib
rake clojure-contrib:new:all     # Do everything for new branch of clojure-contrib
rake clojure:master:all          # Do everything for master branch of clojure
rake clojure:new:all             # Do everything for new branch of clojure
rake default                     # HOWTO use this file

Fine, let's give it a whirl.

$ time rake clojure:new:all
# oodles of lines omitted
ci-build:

BUILD SUCCESSFUL
Total time: 25 seconds
rm -f /.../.clojure/clojure.jar
ln -s /.../clojuggle/src/clojure-new/clojure.jar /.../.clojure/clojure.jar

real    0m55.306s
user    1m10.544s
sys 0m6.873s

Three subtle points here. First, the "ci-build" line is the clue that the local Maven2 repository was updated:

$ ls -l ~/.m2/repository/org/clojure/clojure/
total 8
drwxr-xr-x  7 seths  staff  238 Jan  3 17:57 1.1.0-new-SNAPSHOT
-rw-r--r--  1 seths  staff  322 Jan  3 17:57 maven-metadata-local.xml

Point #2: the ~/.clojure directory has a symlink pointing into the clojuggle/src/clojure-new directory.

$ ls -l ~/.clojure/
total 8
lrwxr-xr-x  1 seths  staff  70 Jan  3 17:57 clojure.jar -> /.../clojuggle/src/clojure-new/clojure.jar

Point #3: a few more commands have surfaced from the Rakefile (e.g. clojure:new:update)

$ rake -T
# ... omitted
rake clojure:blast:it            # Removes files for clojure from $HOME/.clojure and .m2
rake clojure:new:publish         # Push build to local m2 repo and ~/.clojure
rake clojure:new:update          # Update the new branch of clojure

So that's all well and good, but what if you wanted to support Technomancy's fork of Clojure? You would do two things. First this:

$ cat > user.yml
technomancy-clojure:
  clone-from: git://github.com/technomancy/clojure.git
  branches: [master, new]
  use-jar: clojure.jar

And then something like this:

$ rake -T
 # ... omitted
rake technomancy-clojure:master:all  # Do everything for master branch of technomancy-clojure
rake technomancy-clojure:new:all     # Do everything for new branch of technomancy-clojure

DANGER!

Do NOT use this tool to develop on various versions of Clojure. The wipe task always deletes files, so eventually you will lose changes unintentionally. Eventually 'git status' will be used to prevent wiping changes but not yet.

Penultimately, despite all this I still don't have the new branch working with Slime/Swank. It doesn't help that the pom.xml of contrib on the new branch wants 1.1.0-alpha of Clojure. Minor nits though that can be worked out.

Finally, if anyone is still reading and cares about these sort of things, I picked Ruby, v3 of the GPL, and Mercurial/BitBucket because I have strong feelings about those things.

Please respond in some way if you have any questions!

tl;dr: Leiningen is great -- try using it with the new branch of Clojure!

Tagged as: No Comments
   

Foognostic blogs is Digg proof thanks to caching by WP Super Cache