Beautiful Code: Simplicity Yields Power

In Simple Made Easy argues Rich Hickey that mixing orthogonal concerns introduces unnecessary complexity and that we should keep them separate. This mixing sometimes occurs on such a basic level that we believe that there is no other way to do it, an example being the interleaving of polymorphism and hierarchical namespacing represented by OO class hierarchies. Taking those "complected" concerns apart and dealing with them separately yields cleaner, simpler solutions and sometimes also more powerful ones because you are free to combine them as you need and not as the author decided.

A practical example of this simplification by separation of complected concerns is the new Reducers library of collection transforming functions for Clojure. It's very interesting to read its story, no matter whether you're interested in Clojure or not. The old implementation of collection functions such as map, filter and reduce implies a lot: for example the order of processing (sequential), its mechanims (recursion), the representation of the result. On the contrary, this "new super-generalized and minimal abstraction for collections" has only a single requirement for something to be regarded as a collection: "a collection is some set of things that, when given a function to apply to its contents, can do so and give you the result." By not prescribing any unrelated mechanism it opens door to a more flexible and powerful implementation, f.ex. making it possible to process collections in parallel and to compose multiple transformer functions without producing intermediate collections. Elegant and powerful.

Another example of simplicity that brings power is the story of adding support for named parameters to Clojure. Instead of introducing a new construct to the language, Rich took two years to come up with a more elegant and powerful solution that is a natural extension of an existing language feature. He looked at destructuring, i.e. the ability to automatically bind the content of a collection to individual variables (f.ex. specifying a function parameter as "[fname lname]" and calling it with the vector ["Gandalf" "Grey"] would bind the local variable fname to "Gandalf"), and extended it to support maps so that you can automatically bind the value of a key to a local variable. It fits naturally in and is usable and useful in many other contexts than just function calls.

The conclusion is that striving for simplicity and eliminating orthogonal concerns leads to better and occassionally (often?) more powerful solutions.

(Note: This post doesn't expect its readers to know anything about Clojure and thus I opted to use terms and formulations understandable to everybody, even though not completely technically correct.)

You might enjoy also other posts on effective development.

Tags: opinion clojure design

Copyright © 2024 Jakub Holý
Powered by Cryogen
Theme by KingMob