Search code examples
clojure

Eval with local bindings function


I'm trying to write a function which takes a sequence of bindings and an expression and returns the result.

The sequence of bindings are formatted thus: ([:bind-type [bind-vec] ... ) where bind-type is either let or letfn. For example:

([:let [a 10 b 20]] [:letfn [(foo [x] (inc x))]] ... )

And the expression just a regular Clojure expression e.g. (foo (+ a b)) so together this example pair of inputs would yeild 31.

Currently I have this:

(defn wrap-bindings
  [[[bind-type bind-vec :as binding] & rest] expr]
  (if binding
    (let [bind-op (case bind-type :let 'let* :letfn 'letfn*)]
      `(~bind-op ~bind-vec ~(wrap-bindings rest expr)))
    expr))

(defn eval-with-bindings
  ([bindings expr]
   (eval (wrap-bindings bindings expr))))

I am not very experienced with Clojure and have been told that use of eval is generally bad practice. I do not believe that I can write this as a macro since the bindings and expression may only be given at run-time, so what I am asking is: is there a more idiomatic way of doing this?


Solution

  • eval is almost always not the answer though sometimes rare things happen. In this case you meet the criteria because:

    since the bindings and expression may only be given at run-time

    • You desire arbitrary code to be input and run while the program is going
    • The binding forms to be used can take any data as it's input, even data from elsewhere in the program

    So your existing example using eval is appropriate given the contraints of the question at least as I'm understanding it. Perhaps there is room to change the requirements to allow the expressions to be defined in advance and remove the need for eval, though if not then i'd suggest using what you have.