user1552512
user1552512

Reputation: 909

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. :)

Upvotes: 5

Views: 175

Answers (2)

Marcin
Marcin

Reputation: 49826

Here's a functional approach in python:

def reopen(usernames):
    return dict((name, openConnection()) for name in usernames)

You may find it easier to "translate" to a functional style in python before attempting to use a primarily functional language.

Upvotes: 2

Justin Kramer
Justin Kramer

Reputation: 4003

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)))

Upvotes: 6

Related Questions