Edmund Jackson (@edmundjackson) is an independent consultant in all things Clojure and Data Science, and he is a member of LambdaNext, a Clojure training and consulting group.
In the previous post we explored some aspects of how uniformity appears in Clojure, and how this helps dissolve complexity. In this post, we will look at a defining feature of Clojure, its approach to data, which is a study in uniformity.
The OOP Way
The OOP approach to handling data is to put the data into an object and provide limited access to it via accessors. Access needs to be restricted this way due to an underlying problem: mutability. The data represented by the object is changeable, so in order to ensure it is not broken by any function that might need to access it, the access must be intermediated.
So you have taken data, which has an intrinsic shape, locked that away, and instead exposed a projection of that data shape onto accessors. These accessors have likely been designed according to the domain of the problem, rather than the shape of the data, and so they fundamentally change the interaction. This is certainly a useful abstraction, but it does have a problem. Principally, the problem is that it isn’t openly extensible, and it is entirely idiosyncratic.
Extensibility problems quickly arise if you want to modify an object from a library over which you do not have control. If the provided interface can’t support your intended usage as the raw data is locked away, you’re out of luck. This is not an uncommon problem, since objects are oriented around domains of usage, which can’t always be foreseen, and these objects change frequently.
Idiosyncratic access, which is opposite to uniform access, arises as each object author decides which functions to support, how to name them, the style of access, and so on. As a result, the code interacting with data is entirely different depending upon the domain, the library, and the author of the object providing the data, rather than the data shape itself. The result of this is hours drained over Javadocs.
The Clojure Way
The Clojure way is to recognize the incidental complexity generated by the last approach – complexity born of how we choose to represent our problem, rather than the fundamental complexity of it. Data typically comes in very few shapes, and Clojure provides functions around these shapes, rather than the domain from which they arise. Doing this provides uniformity of data manipulation. That is, if we are dealing with something map-like, it shouldn’t matter at all what the map represents or who wrote it, merely what is to be done with it.
Clojure can achieve this by pulling out the root of the problem: mutability. With mutability we are (correctly) concerned about data integrity, and must limit access, thereby creating incidental complexity. If the data is immutable, however, then this problem doesn’t exist. The data itself can be safely passed to any function and it simply can’t be broken. Note that Clojure does not pass a copy of the data, which would be vastly inefficient, but at the level of using the data this is how it can be envisaged.
This means that we can design our algorithms around the intrinsic shape of the data itself. Clojure provides a powerful library of functions around its data structures to empower you to manipulate them in any conceivable way. Furthermore, Clojure can provide destructuring at binding sites, which allows very concise access to data.
For instance, Hiccup represents HTML pages as nested vectors, and so the rich set of functions Clojure provides for dealing with vectors all become available for manipulating web pages. Compare this with an object hierarchy, such as HTMLUnit, where each HTML element is a different class object, and the benefits are immediately clear. Another example is Ring, in which HTTP requests and responses are represented as nested maps. Again, the benefits of being able to use the full set of Clojure’s map manipulation functions become available for this domain. Another great example is Graph, which describes complex computations in order to gain control over their execution. The graph of computations can be manipulated like any other Clojure map. The final example is Clojure code itself, which is a list, and can hence be manipulated with macros, using the rest of Clojure!
Clojure is a language made up of more than the sum of its parts. In this case, by enforcing immutability by default, concerns about data integrity, and the resulting incidental complexity around ensuring it, simply dissolve. As a result, Clojure code can cut out the middleman and deal with the data itself. Since Clojure data comes in rather few shapes, the fundamental objects upon which code acts are uniform across domains, and hence most data manipulation functions can be provided in the core language. This leads to uniformity and simplicity in code bases.
Safari Books Online has the content you need
Check out these Clojure books available from Safari Books Online:
|Clojure Inside Out is a video where you’ll not only learn how to tackle practical problems with this functional language, but you’ll learn how to think in Clojure—and why you should want to. Neal Ford (software architect and meme wrangler at ThoughWorks) and Stuart Halloway (CEO of Relevance, Inc.) show you what makes programming with Clojure swift, surgical, and accurate.|
|Clojure Programming, helps you learn the fundamentals of Clojure with examples relating it to the languages you know already—whether you’re focused on data modeling, concurrency and parallelism, web programming, statistics and data analysis, and more.|
|Practical Clojure is the first definitive reference for the Clojure language, providing both an introduction to functional programming in general and a more specific introduction to Clojure’s features. This book demonstrates the use of the language through examples, including features such as STM and immutability, which may be new to programmers coming from other languages.|
|The Joy of Clojure goes beyond the syntax, and shows how to write fluent, idiomatic Clojure code. You will learn to approach programming challenges from a Functional perspective and master the Lisp techniques that make Clojure so elegant and efficient. This book will help you think about problems the “Clojure way,” and recognize when they simply need to change the way they program.|
About the author
|Edmund Jackson (@edmundjackson) is an independent consultant in all things Clojure and Data Science. Edmund is a member of LambdaNext, a Clojure training and consulting group. Currently based in Cambridge, UK he is looking forward to moving to the US at the end of 2013.|