Two Guys Arguing

clojure refs are functions of their current value

Posted in clojure by youngnh on 09.24.10

I was cleaning up some code that looked like this:

(get-nested (:mappings engine) db-name mapping-name)

Where get-nested was a one-off utility function that returned the value two keys deep in a doubly nested map, like so:

(get-nested {:key1 {:key2 "value"}} :key1 :key2) ;; => "value"

There is a Clojure function that does exactly this, only with slightly different syntax, get-in:

(get-in {:key1 {:key2 "value"}} [:key1 :key2]) ;; => "value"

Imagine my surprise when replacing the original code with

(get-in engine [:mappings db-name mapping-name])

blew up in my face. I checked the definition of get-nested to see if it was doing anything extra I wasn’t aware of:

(defn get-nested [map key1 key2] 
  (if-let [map2 (map key1)] 
    (map2 key2))) 

Nope. Looks pretty standard. It was at this point that my coworker informed me that (:mappings engine) returned a ref holding a map. Now I was really confused. This explained why my get-in form blew up, you can’t get a value from a map if it’s in a ref, you have to deref it first, but the get-nested code had been working in our software in heavy use for weeks.

Turns out, get-nested was very fortuitously written in a style that takes advantage of a little corner of Clojure I hadn’t previously known about. When a ref is invoked as a function, it returns the deref’d value of itself.

It’s not uncommon to write a form like:

({:key1 "value"} :key1)

The map is a function of its keys. Passing it a key returns the corresponding value in the map. This is a form I’m used to seeing. Turns out, this works equally well:

(def narwal (ref {:key1 "value"}))
(narwal :key1)

Since a ref is a function of it’s current value, it gives Clojure whatever object it’s currently holding, and Clojure tries to call invoke on that. It is equivalent to writing:

(@narwal :key1)

But where the first form will always return “value”, refs are updateable and over the course of your program, :key1 could have any value or may not exist at all. The above form is not referentially transparent.

You can see that refs are IFns by:

(supers (class (ref {}))) ;; => #{java.lang.Object java.lang.Runnable clojure.lang.IRef clojure.lang.IDeref clojure.lang.IFn clojure.lang.ARef java.util.concurrent.Callable clojure.lang.IReference clojure.lang.IMeta clojure.lang.AReference java.lang.Comparable}

or dig into the Clojure source code on github and look at the implementation yourself.

Tagged with: , ,

Note to self: C# throw; NOT throw ex;

Posted in .net, java, notes_to_self by benjaminplee on 09.02.10

If you need to catch and re-throw the original exception in C# (and possibly other .Net CLR languages) PLEASE, GOD PLEASE, use:

catch(FooException ex) {
// do what you came here to do
throw;
}

NOT:

catch(BarException ex) {
// do what you came here to do
throw ex;
}

While I love being a polyglot developer equally confident developing on the .Net CLR as the JVM, when I am working in a mainstream OO language I still think in Java.  Thus my confusion today when I found a several times re-thrown exception’s stack trace only going back as far as the last re-throw.  Turns out that explicitly throwing an exception (new or old) sets the originating stack trace location as the point of the throw command, not where the Exception object was created or originally thrown.  This is not the same in Java.  I like the syntactic sugar implicitly throwing the caught exception, but shouldn’t the behaviors be the same!?  Sadly the offending throw anti-pattern is strewn throughout the code I was working with….

** Also, put the damn curly braces on the same line ;-)

*** Several more knowledgeable .Net devs on the team knew this fact, but weren’t 100% sure why; but I found this link which helped.

(Additional) Notes on Remote Pairing

Posted in software development by benjaminplee on 09.02.10

Stuart Sierra of Relevance posted a nice article on their difficulties with and tips for remote pairing.  A few of my thoughts are below (and in a comment on his original article):

My current client was my first experience with remote pairing (local team of developers remote pairing with equal number of client developers). Overall things went much better than I expected however the communication bandwidth difference between remote and local is VERY apparent and a huge loss. Most of the time we try to set up cross-company pairs which benefits reducing knowledge silos, but increases communication costs.

Per the client’s request we have been using Microsoft Communicator with decent success. Lag is an issue but luckily both companies have adequate network speeds to make up for it. Working on a thick-client Windows application means no to terminal work but I will definitely look into tmux and NX next time.

The biggest disadvantage of remote pairing is the loss non-verbal communication and flow. I am a firm believer in the value of pairing in all but the most trivial of tasks, but haven’t been able to muster the same level of interaction over a headset. All of my best development/pairing experiences have been working with a pair which I was “in tune with”. The non-verbal communication (e.g. grunts and quick points) and fluidity (taking quick breaks for a drink, answering a question for another dev in the war room, etc) were the key to our productivity and happiness.

One of the biggest technical issue I have found is war-room spacial awareness while STILL hearing your pair clearly. A high quality headset (mic and earphones) is invaluable when working remotely, but this limits your “presence” in the war room. Side conversations get missed or get replaced by more “formal/slow/inefficient” means of communication. Find a solution to that, and things would be much better.

Pairing is not easy in the best of conditions.  It is so easy for one or both developers to wander off (mentally or otherwise).  On top of convincing developers to open themselves up for constructive criticism and constant communication there is always the adventure that is convincing the business side of things that the “extra” expense now is worth it tomorrow.

Despite all that, it is worth it.  Pairing keeps me pushing myself to work harder/faster/better and leaves no room for bad habits and poor choices.  My first day at Asynchrony I paired for ~8 hours with a great developer and promptly came home to my wife and collapsed.  She, being the loving wife she is, was concerned my first day had gone poorly.  I said no.  Great in fact.  I wrote more high quality code that day than I had in the past month working in a cube by myself.  My brain was just dead tired.

</end psuedo-rant>

Follow

Get every new post delivered to your Inbox.