1

I am trying to write a function eval-with-bindmap which takes two parameters, an expression to evaluate and a map of symbols to values which will act as bindings in the evaluation. I cannot use let since the bindings may be in any order in the map (it should work with both array-maps and hash-maps) and it may also contain bindings of functions which are mutually recursive (as in letfn). For example:

(eval-with-bindmap '(+ a b) '{a (+ 1 b) b 2}) => 5

Any tips on how to approach this would be awesome, thank-you.

3
  • Why do you want to allow the bindings to be in any order? What if there is a circular dependency? If you really want to allow that, you'd basically first need to implement an algorithm to ensure a topological sorting. Commented Jan 16, 2018 at 14:43
  • Since this function needs to work with hash-maps the bindings may be in any order. We can assume that there will be no direct circular dependencies in the binding map however there may be symbols bound to functions which call other functions in the binding (like you can in letfn). Commented Jan 16, 2018 at 14:59
  • What about security? where does the expression come from? Commented Jan 22, 2018 at 16:38

2 Answers 2

1

Some simple recursive processing could work (unless your form expansion is really deep, otherwise you should think about tail recursive version)

(defn eval-with-bindings [form bind]
  (cond (seq? form) (apply (resolve (first form))
                           (map #(eval-with-bindings % bind) (rest form)))
        (and (symbol? form) (contains? bind form)) (eval-with-bindings (bind form) bind)
        :else form))

user> (eval-with-bindings '(+ a b) '{a (+ 1 b) b 2})
5

in case of infinite circular deps it would just fall with stackoverflow:

user> (eval-with-bindings '(+ a b) '{a (+ 1 a) b 2})
StackOverflowError   clojure.lang.AFn.applyToHelper (AFn.java:148)
Sign up to request clarification or add additional context in comments.

1 Comment

There is only rudimentary tail recursion in Clojure because of the JVM (using recur), and tail recursion is only of limited use for tree descent anyway.
1

I'd say, the better approach would be still to use eval inside let, but process your binding maps into let vectors in a special way that cares about a proper order.

For example, say you've got a map {a (+ 1 b) b 2}. For each key/expression pair, you check if an expression includes any alpha-numeric symbols. The b would be our candidate. In that case, you move b 2 before the a (+ 1 b) and so on.

Yes, the proper algorithm might be a bit complicated, but then you won't have any troubles with eval in further.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.