Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I build a list and return it in clojure?

I'm still learning this alien functional paradigm...

How would I write the following code in Clojure, and in a functional way? assume this missing parts are defined elsewhere and behave as described in the comments. Here it is in Python, which I am familiar with.

usernames = []
# just the usernames of all the connections I want to open.
cancelfunctions = {}
# this global contains anonymous functions to cancel connections, keyed by username

def cancelAll():
    for cancel in cancelfunctions.values():
        cancel()

def reopenAll():
    cancelfunctions = {}
    for name in usernames:
        # should return a function to close the connection and put it in the dict.
        cancelfunctions[name] = openConnection()

All I really need to know is how to build up a new dict of callbacks, like in the reopenAll function, but I'm including some more context here because chances are I'm committing some kind of functional paradigm atrocity, and you will most likely want to fix the whole program. :)

like image 792
user1552512 Avatar asked Aug 13 '12 19:08

user1552512


1 Answers

Building data structures in Clojure often involves reduce, which feeds a sequence of inputs to a function which accumulates a final return value. Here are two ways to write a function which constructs a map (i.e. dictionary) of username to the return value of open-connection.

;; Using reduce directly
(defn reopen-all [usernames]
  (reduce
   (fn [m name] (assoc m name (open-connection)))
   {} usernames))

;; Using into, which uses reduce under the hood
(defn reopen-all [usernames]
  (into {} (for [name usernames]
             [name (open-connection)])))

Note that these two functions return a value and do not mutate global state, as your Python code does. Global state isn't inherently bad, but it's good to separate value-generation from state manipulation. For state, you'll probably want an atom:

(def usernames [...])
(def cancel-fns (atom nil))

(defn init []
  (reset! cancel-fns (reopen-all usernames)))

And here's cancel-all for completeness' sake:

(defn cancel-all []
  (doseq [cancel-fn (vals @canel-fns)]
    (cancel-fn)))
like image 55
Justin Kramer Avatar answered Oct 18 '22 07:10

Justin Kramer