7

I'm writing a simple game in Java and I want to learn Clojure, so I've decided to refactor my current Java code to Clojure. The problem is that I've coded so much in object-oriented languages that I cannot see how to do it functionally.

To be concrete, I have a Map<String, Country> inside a singleton class that can be accessed from anywhere to get a Country instance, update it, and put it back into the map. I implemented this the same way in Clojure. For example, to update a country:

(def countries (do-get-countries))

(defn update-country
    [country]
    (def countries (assoc countries (get country :name) country)))

Also I've created a defrecord Country, but I actually modify these records like

(assoc country :name "New name")

These two examples don't look idiomatic in my opinion. Is this actually the correct way to do it in Clojure? If not how would it be more idiomatic?

Thanks in advance!

  • 1
    possible duplicate of How to efficiently refactor Java to Clojure? – user16764 Nov 29 '13 at 1:15
  • 3
    The duplicate flag was also raised the other way around. I suggest leaving this incarnation open as it already has an answer and the other doesn't. – Bart van Ingen Schenau Nov 29 '13 at 8:01
  • Singleton is not really Object Oriented approach, and considered to be anti pattern in some circles. – David Sergey Dec 9 '13 at 19:11
  • @nirth I know, I've read extensively about it, but as with everything in programming, you can use it the right way or the wrong way. Actually in my case it's more an object factory than a singleton. – m0skit0 Dec 9 '13 at 19:20
12

Here's a step-by-step guide to getting from your Java example to some decent Clojure:

  1. recognize that your singleton is just global, mutable state. The singleton may work fine but it's not necessary. So we've moved from Java: singleton -> Java: global, mutable state
  2. refactor your Java example to use local mutable state instead of global mutable state, by passing the map to the methods that modify it. So let's move from Java: global, mutable state -> Java: local, mutable state
  3. *now, instead of destructively updating the map every time, find/write a Java library (such as the one the Clojure implementation uses) that does not mutate when adding/removing key/value pairs to/from maps. Remember to return values that have been "updated", otherwise, the changes won't be visible to other code. So we've just moved from Java: local, mutable state -> Java: local, immutable state
  4. at this point, you have an FP solution, but it's coded in Java. Your initial Clojure solution could end up as a nearly 1:1 translation, but as you learn more Clojure you'll figure out how to take advantage of its strengths and improve and shorten the code. Plus, you might learn some cool patterns for dealing with "mutable" state in a purely functional way. So it's up to you to make the leap from Java: local, immutable state -> Clojure: local, immutable state

From point 3 above: one of the major points of FP is "same thing in, same thing out". Mutable state totally destroys this concept, and with it, the advantages of pure code. Clojure doesn't force you to be pure, but it certainly makes it easy to do so. So if you want to learn how to write good Clojure code, you'll have to learn how to avoid (most) mutable state at some point.

  • Yes, I totally agree and precisely this is what I did yesterday. I pass the map to functions in Clojure so functions always return same result for same input. This removed any kind of state and looks much better now. Thanks for taking time to answer! – m0skit0 Dec 3 '13 at 11:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.