clojureclojure-contrib

A Way To Gather All Bindings Of A Clojure Let-Expression


I'm finding myself writing a function that passes every variable bound in the enclosing let expression. The simplified form of this scenario, in psuedocode, is

(let [a 1 b 2 c 3 d 4 e 5 f 6]
  (do-something a b c d)
  (f a b c d e f g)
  (compute-another-thing a c))

whereas I want to write something more like...

(let env-name [a 1 b 2 c 3 d 4 e 5 f 6]
   (do-something (take 4 env-name))
   (f env-name)
   (compute-another-thing a c))

That is, does the clojure core (or something external and reliable) provide a let variant that gathers all bindings into a collection with a name?


Solution

  • Destructuring will get you part of the way there:

    (let [[a b c d e f :as env-name] [1 2 3 4 5 6]]
       (apply do-something (take 4 env-name))
       (apply f env-name)
       (compute-another-thing a c))
    

    Note you have to use apply when calling functions using env-name.

    For something more elegant (like your proposed form), I think you would have to write your own macro.

    Update: Here's a (very-lightly tested) macro that can handle simple cases:

    (defmacro let-as 
      [as-bind-name bindings & body]
      (let [lhs-bindings (map first (partition 2 bindings))]
        `(let ~bindings
           (let [~as-bind-name [~@lhs-bindings]]
                 ~@body))))
    

    This should do what you want so long as the left-hand side of your normal let-bindings are simple symbols (ie don't use destructuring).

    Some REPL interactions to illustrate:

    core> (let-as env-name [a 1 b 2 c 3 d 4 e 5 f 6] (print env-name) (apply + env-name))
    [1 2 3 4 5 6]
    21
    

    Some destructuring is well-behaved:

    core> (let-as env-name [[a b] [1 2] [c d] [3 4]] (print env-name) (map first env-name))
    [[1 2] [3 4]]
    (1 3)
    

    Other destructuring is not:

    core> (let-as env-name [{:keys [a b]} {:a 1 :b 2}] (print env-name) (+ a b))
    [{:keys [1 2]}]
    3
    

    This could be remedied, but the macro would have to be more complex