Support for destructuring in clojure/binding

7 views
Skip to first unread message

Stephen C. Gilardi

unread,
Sep 21, 2008, 10:36:30 PM9/21/08
to Clojure
I was surprised that clojure/binding doesn't support destructuring bind:

Clojure
user=> (def a)
#'user/a
user=> (def b)
#'user/b
user=> (binding [[a b] [1 2]] (+ a b))
java.lang.ClassCastException: clojure.lang.LazilyPersistentVector cannot be cast to clojure.lang.Symbol (NO_SOURCE_FILE:3)
user=> 

I took a look at implementing it and it looks non-trivial. The vector emitted by destructure contains a mixture of bindings for generated symbols and bindings for the target symbols. If one could reliably identify the generated symbols, it would be possible to put the call to var-ize inside a destructured let of the same bindings to produce, e.g., {(var a) a (var b) b}, etc. to pass along to pushThreadBindings.

If it's feasible, it's a feature I'd like to see in Clojure at some point.

--Steve

Stephen C. Gilardi

unread,
Sep 22, 2008, 12:42:23 AM9/22/08
to clo...@googlegroups.com

On Sep 21, 2008, at 10:36 PM, Stephen C. Gilardi wrote:

If it's feasible, it's a feature I'd like to see in Clojure at some point.

It looks like tagging the gensym'd symbols with metadata will work. I'm having a go at it.

--Steve

Stephen C. Gilardi

unread,
Sep 22, 2008, 2:27:28 AM9/22/08
to clo...@googlegroups.com
On Sep 22, 2008, at 12:42 AM, Stephen C. Gilardi wrote:

It looks like tagging the gensym'd symbols with metadata will work. I'm having a go at it.

Stephen C. Gilardi

unread,
Sep 28, 2008, 11:25:22 PM9/28/08
to clo...@googlegroups.com
On Sep 22, 2008, at 2:27 AM, Stephen C. Gilardi wrote:


I updated the patch to improve readability and uploaded it to:


--Steve

Stephen C. Gilardi

unread,
Oct 8, 2008, 11:38:34 PM10/8/08
to clo...@googlegroups.com
Rich,

This is code from my socket repl that sparked my interest in support
for destructuring in clojure/binding.

(defn socket-streams ;; returns a vector of 3 streams
[s]
[(LineNumberingPushbackReader. (InputStreamReader. (.getInputStream
s) "UTF-8"))
(OutputStreamWriter. (.getOutputStream s) "UTF-8")
(PrintWriter. (OutputStreamWriter. (.getOutputStream s) "UTF-8")
true)])

(defn socket-repl ;; accepts a vector of 3 streams with minimal
fuss
"Starts a repl thread on the iostreams of supplied socket"
[s]
(on-thread
#(binding [[*in* *out* *err*] (socket-streams s)] (SocketRepl/
repl))))

I've updated the patch I posted previously. I think this
implementation is very readable and a nice example of how much
destructuring, metadata, and reader support for a variety of
collections can contribute to making Clojure code more clear and
compact.

http://clojure.googlegroups.com/web/destructuring-binding.patch

Please consider including it in Clojure.

Thanks,

--Steve

Krukow

unread,
Oct 9, 2008, 3:13:50 AM10/9/08
to Clojure


On Sep 22, 4:36 am, "Stephen C. Gilardi" <squee...@mac.com> wrote:
> I was surprised that clojure/binding doesn't support destructuring bind:
>
> Clojure
> user=> (def a)
> #'user/a
> user=> (def b)
> #'user/b
> user=> (binding [[a b] [1 2]] (+ a b))

Hello Stephen.

I am not sure I understand. Your example doesn't seem to suggest that
you are really needing a form of destructuring. Rather, it looks like
multiple bindings. Presently, I can do:

krukow:~/languages/clojure/trunk$ cl
Clojure
user=> (def a)
#'user/a
user=> (def b)
#'user/b
user=> (binding [a 1 b 2] (+ a b))
3
user=>

Would that be enough?

Kind regards,
- Karl

Stephen C. Gilardi

unread,
Oct 9, 2008, 9:03:44 AM10/9/08
to clo...@googlegroups.com

On Oct 9, 2008, at 3:13 AM, Krukow wrote:

> Hello Stephen.
>
> I am not sure I understand. Your example doesn't seem to suggest that
> you are really needing a form of destructuring. Rather, it looks like
> multiple bindings. Presently, I can do:
>
> krukow:~/languages/clojure/trunk$ cl
> Clojure
> user=> (def a)
> #'user/a
> user=> (def b)
> #'user/b
> user=> (binding [a 1 b 2] (+ a b))
> 3
> user=>
>
> Would that be enough?

Hi Karl,

You're right, the example I gave was simplified to the point where it
could be done as you've shown.

Other binding forms in Clojure (fn arguments, let, loop) support some
very nice destructuring features -- names can be bound to parts of
"sequential things" or parts of "associative things" in a very
flexible and readable way.

My patch allows those same features to be used with vars in binding.
One of the nice uses of Clojure's destructuring is to provide most of
the benefit of returning multiple values from a function without
straying from the Java calling convention of a single return value--a
function can pass back a vector, but the caller can still end up with
names for each of the components easily.

Here's a simplified version of a better example from a later posting:

(defn socket-streams ;; returns a vector of 3 streams
[s]

[(InputStreamReader. (.getInputStream s) "UTF-8"))


(OutputStreamWriter. (.getOutputStream s) "UTF-8")

(OutputStreamWriter. (.getOutputStream s) "UTF-8") )])

(defn socket-user ;; binds the vector of 3 streams to vars with
minimal fuss
"Use the iostreams of a supplied socket"
[s]
(on-thread
#(binding [[*ins* *outs* *errs*] (socket-streams s)]
(stream-using-func))))

This could be done right now with a let to do the destructuring and a
bind to the individual values to vars, but my patch makes that
unnecessary/automatic and makes all Clojure binding forms consistent
in supporting destructuring.

Cheers,

--Steve

Reply all
Reply to author
Forward
0 new messages