Alexander Popov
Alexander Popov

Reputation: 24875

How to bind function variables to current scope of execution in Clojure?

Given:

(defn some-fn
  []
  (let [a 1
        b 2
        c (+ a b)]
    (println c)))

and given that there are multiple such functions, where:

is there a way to extract c without making it a function, which accepts a and b as arguments. So, I don't want:

(defn c-outside
  [a b]
  (+ a b))


(defn some-fn
  []
  (let [a 1
        b 2
        c (c-outside a b)]
    (println c)))

but ideally, something like:

(defn c-outside
  []
  (+ a b))


(defn some-fn
  []
  (let [a 1
        b 2
        c (c-outside)]
    (println c)))

Is there a way to make c-outside look for the values of a and b in the context, in which it is called? Do I need a macro for that?

Upvotes: 0

Views: 110

Answers (1)

leetwinski
leetwinski

Reputation: 17859

there is a way to do it using dynamic bindings:

user> (def ^:dynamic a 10)
#'user/a

user> (def ^:dynamic b 20)
#'user/b

user> (defn c-outside []
        (+ a b))

user> (defn some-fn []
        (binding [a 1
                  b 2]
          (c-outside)))
#'user/some-fn

user> (some-fn)
;;=> 3

user> (c-outside)
;;=> 30

the trick is that you can temporarily rebind some dynamic vars for the 'duration' of some scope. This is mostly used in clojure for concurrent programming: the dynamically bound vars keep their values in the threads, spawned from inside the block (as far as i remember)

Otherwise, i see more potential harm from this feature, than the profit, since it obscures the code, adding some unneeded implicit behaviour.

Also as far as i know, this is one of the most arguable features in lisps (in common lisp, to be more specific)

Almost in any case it is better to pass a and b values explicitly to the summing function, since it makes in clean and therefore testable, and helps to reason about it's correctness and performance, and increases readability

you could also think of using macro for that, like this for example:

user> (defmacro c-outside-1 []
        `(+ ~'x ~'y))
#'user/c-outside-1

user> (defn some-fn-1 []
        (let [x 1
              y 2]
          (c-outside-1)))
#'user/some-fn-1

user> (some-fn-1)
;;=> 3

but that idea is obviously even worse.

Upvotes: 1

Related Questions