zenna
zenna

Reputation: 9176

Clojure practice - use functions of complex datatypes or their elements?

It is idiomatic in lisps such as Clojure to use simple data-structures and lots of functions. Still, there are many times when we must work with complex data-structures composed of many simpler ones.

My question is about a matter of good style/practice. In general, should we create functions that take the entire complex object, and within that extract what we need, or should they take exactly and only what they need?

For concreteness, I compare these two options in the following psuedo code

(defrecord Thing
  [a b])

(defn one-option
  [a]
  .. a .. ))

(one-option (:a a-thing))

;; ==============

(defn another-option
  [a-thing]
  .. (:a a-thing) .. ))

The pros of one-option-f is that the function is simpler, and has fewer responsibilities. It is also more compositional - it can be used in more places.

The downside is that we may end up repeating ourselves. If, for example, we have many functions which transform one Thing into another Thing, many of which need use one-option, within each one we will find ourselves repeating the extraction code. Of course, another option is to create both, but this also adds a certain code overhead.

Upvotes: 3

Views: 141

Answers (2)

Hendekagon
Hendekagon

Reputation: 4643

Do both. To begin with, provide functions that transform the simplest structures, then add convenience functions for handling more complex structures which compose the functions that handle the simple ones.

Upvotes: 0

Thumbnail
Thumbnail

Reputation: 13483

I think the answer is "It depends" and "It doesn't matter as much in Clojure as it does in object systems". Clojure has a number of largely orthogonal mechanisms designed to express modes of composition. The granularity of function arguments will tend to fall out of how the structure of the program is conceived of in the large.


So much for the hot air. There are some specifics that can affect argument granularity:

  • Since Clojure data structures are immutable, data hiding and access functions/methods have little relevance. So the repetition caused by accessing parts of a complex structure is trifling.
  • What appear in object-design as associations are rendered by small collections of typed object pointers in each object. In Clojure, these tend to become single(ton) global maps. There are a standard Clojure functions for accessing and manipulating hierarchical structures of this kind (get-in and assoc-in, for examples).
  • Where we are looking for dynamically-bound compliance with an interface, Clojure protocols and datatypes are cleaner and more adaptable than most object systems. In this case, the whole object is passed.

Upvotes: 1

Related Questions