9 August 2021

Destructuring in API Design

Applying destructuring to smooth out helper methods.

I've been working on preparing a kata for performance. Today I had the opportunity to get feedback from my coworkers. One bit of advice I got had to do with applying sequential destructuring to a few helper methods that got extracted during the course of the presentation. (Thanks Gina!) Here are the functions in their original form:

(defn is-strike? [rolls]
  (= 10 (first rolls)))

(defn is-spare? [rolls]
  (= 10 (+ (first rolls) (second rolls))))

These functions bear some resemblance to the canonical examples in that they access and assign individual elements of a sequence. Clojure destructuring allows a function to bind elements of a collection to named values as it is being received. This saves the developer the inconvenience of defining a let block or needing to accessing the elements directly every time they are used (which is what the code above does).

Here's how the methods look after applying destructuring:

(defn is-strike? [[first & rolls]]
  (= 10 first))

(defn is-spare? [[first second & rolls]]
  (= 10 (+ first second)))

These examples don't consume all elements in the provided rolls collection, so the & indicates that the un-destructured elements should reside in the named value immediately proceeding it. In the case of these functions, the remaining 'tail' collection is not used, so we needn't assign it a meaningful name:

(defn is-strike? [[first & _]]
  (= 10 first))

(defn is-spare? [[first second & _]]
  (= 10 (+ first second)))

We've traded complexity in the function body with a bit more definition in the function signature--a fair trade in this case I think.