Question

Being new to clojure I struggle with finding an idiomatic style for different code constructs.

In some cases my let bindings contain most of the code of a function. Is this bloat, some misunderstanding of clojure philosophy or idiomatic and just fine?

Here is a sample testcase to demonstrate. It tests an add/get roundtrip to some storage repository. Does the long let look wierd?

(deftest garden-repo-add-get
  (testing "Test garden repo add/get"
    (let [repo (garden/get-garden-repo)
          initial-garden-count (count (.list-gardens repo))
          new-garden (garden/create-garden "Keukenhof")
          new-garden-id (.add-garden repo new-garden)
          fetched-garden (.get-garden repo new-garden-id)]
      (is (= (+ initial-garden-count 1) (count (.list-gardens repo))))
      (is (= (.name new-garden) (.name fetched-garden))))))
Was it helpful?

Solution

Main problem I see with your let code, and is the usual case, is you're using lots of intermediate variables whom only have names in order to exist in the let form.

The best approach to avoid the overbloat is to use the arrow macro -> or ->>

For instance you can avoid repo intermediate variable with

initial-garden-count (-> (garden/get-garden-repo)
                         (.list-gardens)
                         count)

None the less, in your particular case you're using all your intermediate variables in your test validation, so you need them on the let statement anyway. Maybe new-garden-id is the only intermediate you can avoid:

fetched-garden (->> (.add-garden repo new-garden)
                    (.get-garden repo))

Or using the approach suggested by Chiron:

fechted-gaden (.get-garden repo (.add-garden repo new-garden))

OTHER TIPS

Personally, that is how I code in Clojure. Clojure is terse, elegant and concise enough.
I don't think that by compacting to the following is going to be "idiomatic".

  (is (= (+ (count (.list-gardens repo)) 1) (count (.list-gardens (garden/get-garden-repo)))))

Your code snippet is easier to understand, to test to debug (in Eclipse, IntelliJ ..).

Clojure is about simplicity and I would like to keep it that way.

Still, answers to your question are highly opinionated.

By looking at your function code it seems that the "things" the function is working with are typical OO objects with getter/setter and methods (see there are lots of .{something} calls), which makes you use these let bindings because of the way the object APIs are designed. Which is fine if you have to work with objects.

In Clojure you basically design your APIs around data structures like map/vector/list/set and functions, which (in most cases) gives you the sort of API which may not require that much let binding usage.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top