Had seen many instances, in which there are unbelievably concise Clojure code solves a problem, advocating for "Functional Thinking". Being a java developer, I have seen and wondered at it. But didn’t get into much into it.
Recently was reading about Conway’s game of life.
To give a glimpse, we will have a initial 2D grid in which we will have cells. Each cell can be live/dead. The cells can continue in its state or toggle to live/dead based on the state of the cells around it. The state of whole grid evolves to next state, as all cells in it evolve. This automata need to be programmed. Not a complicated problem, but takes a while to write it as a program.
Really got shocked when I see this 7 line solution in Clojure. I had to write nearly a 100 line of code in Java to do the same.
Took this chance to analyze where "exactly" this conciseness comes from. Is it in the way the problem was solved?, Is it in the constructs that language provides?, or Is it some magic of functional programming?.
This article is not to summarize on conciseness of Clojure, but to explore it on a particular problem. So this will just talk about conciseness in a small context.
So I tried to solve it in Java in the way a typical Java programmer(at least me) solves it. Leaving the comments and empty new lines, the actual code is around 100 lines. Nearly 15 times as long as the Clojure’s code.
Then compared my code against the clojrue version and came up with version2 with a bit of logical optimization. That hardly reduced 10 lines of code.
Then tried implementing the logic in the same way it is implemented in above-mentioned Clojure version and came up with version 3.
Since it could be too much to explain the solution in detail for each and every version in contrast with the Clojure version, I am leaving it upto the reader to understand and I’m jumping to the result that summarizes the core differences.
Representing the grid: In my Java v1 and v2, I used a boolean matrix to represent the cellular grid and the live status is represented by boolean. Whereas, in the Clojure version they maintained a only the coordinates of the live cells. That’s a concise way of representing a grid where each element in the grid can take only 2 states.
Calculating the next state: In my Java v1, I literally translated the logic that is being asked for. But Clojure version minimized the calculation happens.
Representation of coordinates: In all Java versions, I wrote a separate class that represents a coordinate. Of course I could have used a simple array to do this but that’s not the way how I think as a Java developer. But in Java v3, I had to write a class for coordinate, overriding the hashcode
and equals
. So that I can leverage on the HashSet
, to have the coordinates de-duplicated. In the case of Clojure, the representation of any collection is so concise. Further, we have an out-of-box equality check for collections that does it by comparing each element in it.
List Comprehension: List comprehension is a nice feature offered by languages like Clojure, Python, etc.., which provides a concise way of iterating through multiple collections and returning another collection as a result. To do the same in Java you must use a loop or nested loops. For ex, in this context, the method getNeighbours
in the java versions had to have a nested for-loop
along with object creation and other stuff, whereas in Clojure its just a list comprehension in the method neighbors
.
Utility functions: In this problem context, Clojure has pre-existing functions like repeat
, frequencies
and mapcat
, which seems to be so rudimentary, but in Java we have to implement it on our own.
The conciseness arises from 'way of thinking' and the 'language constructs'. As I read, try and understand will continue to explore more on this front.
For any feedback or corrections, please write in to: kannangce@rediffmail.com