binding and bundles of variables

16 views
Skip to first unread message

samppi

unread,
Aug 7, 2009, 1:12:20 AM8/7/09
to Clojure
I have about six variables that are often rebound together using
binding; these variables are used to access and set data in a state
object (whose type is of the user's choice). These variables' values
(the accessors and setters) are often used together.

I'd like to be able to bundle these values into a map, and using a
function or macro called with-bundle, automatically bind the map's
vals to their respective variables:
(def my-bundle
{'*variable-1* :something1, '*variable-2* :something2})

(with-bundle my-bundle
(do-stuff))
; equivalent to (binding [*variable-1* :something1, *variable-2*
something2] (do-stuff))

(Why don't I just use one overridable variable containing a map?
Backwards compatibility with my library's previous versions, which
already use these multiple variables.)

So is this possible without arcane stuff? In
http://groups.google.com/group/clojure/browse_thread/thread/23fe1a5c9d940976/,
I asked about using a macro to do this, and the consensus seems to be
that it's possible with magic, but it's generally a bad idea. So what
should I do—is there anything I can do, other than using only one map
variable?

Meikel Brandmeyer

unread,
Aug 7, 2009, 1:56:17 AM8/7/09
to Clojure
Hi,

On Aug 7, 7:12 am, samppi <rbysam...@gmail.com> wrote:

> So is this possible without arcane stuff? Inhttp://groups.google.com/group/clojure/browse_thread/thread/23fe1a5c9...,
> I asked about using a macro to do this, and the consensus seems to be
> that it's possible with magic, but it's generally a bad idea. So what
> should I do—is there anything I can do, other than using only one map
> variable?

And in the above thread I wrote the following:

For this special case however you can solve the issue
as follows:

(defn with-bundle*
[the-map thunk]
(clojure.lang.Var/pushThreadBindings the-map)
(try
(thunk)
(finally
(clojure.lang.Var/popThreadBindings))))

(defmacro with-bundle
[the-map & body]
`(with-bundle* ~the-map (fn [] ~@body)))

Hope this helps.

Sincerely
Meikel

samppi

unread,
Aug 7, 2009, 8:52:41 PM8/7/09
to Clojure
Great, thanks. Is clojure.lang.Var/pushThreadBindings a public,
supported part of the API? Can I use it without fear of suddenly
dropped support?

Meikel Brandmeyer

unread,
Aug 8, 2009, 5:53:55 AM8/8/09
to clo...@googlegroups.com
Hi,

Am 08.08.2009 um 02:52 schrieb samppi:

> Great, thanks. Is clojure.lang.Var/pushThreadBindings a public,
> supported part of the API? Can I use it without fear of suddenly
> dropped support?

It is was `binding` uses internally. Unfortunately
this is not exported by Clojure's public API. I -
unfortunately - have to rely on this hack.

Sincerely
Meikel

Rich Hickey

unread,
Aug 8, 2009, 9:52:30 AM8/8/09
to clo...@googlegroups.com

get-/push-/pop-thread-bindings wrapping Var.get/push/popThreadBindings
would be a welcome issue/patch.

Note the addition of getThreadBindings(), which returns a map of all
the current bindings. This could be used to define a
function-returning macro that can be used when you want to pass a
helper function to another thread and have it use the bindings in
effect at the point of its creation.

If someone wants to propose a patch for the latter we can discuss in
clojure-dev.

Rich

Meikel Brandmeyer

unread,
Aug 8, 2009, 2:51:45 PM8/8/09
to clo...@googlegroups.com
Hi,

Am 08.08.2009 um 15:52 schrieb Rich Hickey:

> get-/push-/pop-thread-bindings wrapping Var.get/push/popThreadBindings
> would be a welcome issue/patch.
>
> Note the addition of getThreadBindings(), which returns a map of all
> the current bindings. This could be used to define a
> function-returning macro that can be used when you want to pass a
> helper function to another thread and have it use the bindings in
> effect at the point of its creation.
>
> If someone wants to propose a patch for the latter we can discuss in
> clojure-dev.

Ok. Will give it a try and post me results on clojure-dev.

Sincerely
Meikel

John Harrop

unread,
Aug 8, 2009, 5:24:44 PM8/8/09
to clo...@googlegroups.com
It occurs to me that the above would also allow stored functions to carry thread-local bindings with them. The obvious application being to fix the interaction between thread-local bindings and lazy seqs. They could also potentially even be used in code run in a future.

jvt

unread,
Aug 9, 2009, 9:24:36 PM8/9/09
to Clojure
When I encountered this post, my instinct was to suggest that he write
a specific macro to bind these particular variables rather than depend
on magic to make new bindings at run-time from symbols known only at
run-time, a la:

(defmacro with-a-b-c [m & body] `(let [mp# ,mp] a (:a mp#) b (:b mp#)
c (:c mp#)] ~@body))

Is this a more "idiomatic" solution or a more "lispy" one, or am I
laboring under a misunderstanding?

-V
On Aug 8, 5:24 pm, John Harrop <jharrop...@gmail.com> wrote:

Meikel Brandmeyer

unread,
Aug 10, 2009, 1:07:14 AM8/10/09
to clo...@googlegroups.com
Hi,

Am 10.08.2009 um 03:24 schrieb jvt:

> (defmacro with-a-b-c [m & body] `(let [mp# ,mp] a (:a mp#) b (:b mp#)
> c (:c mp#)] ~@body))
>
> Is this a more "idiomatic" solution or a more "lispy" one, or am I
> laboring under a misunderstanding?

I find this not very elegant. Tomorrow you need b, c and d,
so you write another macro. Then you f, g, a and x and write
another macro.... As a minor point: above you probably meant
binding for a, b and c.

Having a solution, which solves this problem once and for all,
is more interesting.

Sincerely
Meikel

Niels Mayer

unread,
Aug 9, 2009, 11:58:42 PM8/9/09
to clo...@googlegroups.com
On Thu, Aug 6, 2009 at 10:12 PM, samppi <rbys...@gmail.com> wrote:

I have about six variables that are often rebound together using
binding; these variables are used to access and set data in a state
object (whose type is of the user's choice). These variables' values
(the accessors and setters) are often used together. 
I'd like to be able to bundle these values into a map, and using a
function or macro called with-bundle, automatically bind the map's
vals to their respective variables:
(def my-bundle
{'*variable-1* :something1, '*variable-2* :something2})
 
If you don't get too religious about the curlyness of your brackets or the inelegance of the syntax (which is basically common-lisp keywords, using "infix notation," with the entailed loss of elegance caused by that choice)...  What about using JSON, which seems to be close to the syntax you're already using. Because JSON is ultimately a matter of calling a Java class,all the work has already been done in terms of going back and forrth between a "map" datastructure and the text-representation, both for JSON and XML.

For example (cut/paste from http://www.javapassion.com/ajax/JSON.pdf )

JSON looks like:
 var myJSONObject = {"bindings": [
{"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"},
{"ircEvent": "PRIVMSG", "method": "deleteURI", "regex": "^delete.*"},
{"ircEvent": "PRIVMSG", "method": "randomURI", "regex": "^random.*"}
]
};

• Members can be retrieved using dot or subscript operators
myJSONObject.bindings[0].method // "newURI 

In Java, the same "dot notation" can be used for set/get, but use JSONObject Java Class
• A JSONObject is an unordered collection of name/value pairs
• The put methods adds a name/value pair to an object
• The texts produced by the toString methods strictly conform to the JSON syntax rules

Output:
myString = new JSONObject().put("JSON", "Hello, World!").toString();
// myString is {"JSON": "Hello, World"}

Parsing:
try {
jsonObject = new JSONObject(myString);
}
catch(ParseException pe) {
}

.....

Similarly you could probably also use whatever combination of XML libraries that'll turn XML structures into
data structures that can easily be accessed in Java, for example, here's a little script you can throw into Xwiki to grab a RSS feed and turn it into something that can easily be accessed in Java, Groovy, or Clojure:

Takes URL param ?zip=ZIPCODE and gets associated weather report for that zipcode:

{{script language=groovy}}
  def rss = new XmlSlurper().parseText(("http://weather.yahooapis.com/forecastrss?p="
                                        + xcontext.macro.params.zip).toURL().text)
  println rss.channel.title
  println "Sunrise: ${rss.channel.astronomy.@sunrise}"
  println "Sunset: ${rss.channel.astronomy.@sunset}"
  println "Currently:"
  println "\t" + rss.channel.item.condition.@date
  println "\t" + rss.channel.item.condition.@temp
  println "\t" + rss.channel.item.condition.@text  
{{/script}}
 
Niels

PS: what are people's favorite JSON or XML Java classes for doing this out of scripting languages like Groovy or Clojure? Basically, going back and forth between a structured textual representation and a map data structure that can be accessed by variables like "rss.channel.item.condition.text" . 

jvt

unread,
Aug 10, 2009, 3:10:57 PM8/10/09
to Clojure
Meikel,

What concerns me is that this macro lets you write code which depends
on names which are not present at compile time (someplace). Coming
from scheme, not only would you _not_ do this, but you _can't_ do it
without using eval in your macro body, which is considered bad form.
That we can do it in clojure appears to be a consequence of the fact
that 1) clojure uses maps internally for binding and 2) that thread-
local binding requires you declare the vars somewhere before using
them someplace else.

Be this as it may, you can still get weird run-time behavior from this
kind of macro in the following situation:

(def *v1* 10)
(def var-map {:*v1* 100})
(def wrong-map {:*v2* 10})

(with-bindings-from-map var-map (+ v1 100)) ; -> 200, does what we
expect
(with-bindings-from-map wrong-map (+ v1 100)) ; -> 110, does the
wrong thing entirely, silently

Writing a separate macro for each kind of binding may be slightly less
convenient, but it also gives us a smarter error message.

we could write instead

(defmacro binding-v1 [ mp & body ] `(binding [{*v1* :*v1*} ~mp]
~@body))

(binding-v1 var-map (+ *v1* 10)) ; works
(binding-v1 wrong-map (+ *v1* 10)) ; this gives the error we expect,
that there is no *v1* in wrong-map. (ok, it gives an error when we
try to add 10 to nil, but error checking in binding-v1 would give a
better error).

Plus, this doesn't depend on a how the compiler implements or
optimizes names. It could be that in the future clojure will want to
use a different representation internally than maps for symbol->value
translation.

-V

On Aug 9, 11:58 pm, Niels Mayer <nielsma...@gmail.com> wrote:
> Nielshttp://nielsmayer.com

jvt

unread,
Aug 10, 2009, 3:26:17 PM8/10/09
to Clojure
Really, this should be

(with-bindings-from-map var-map (+ *v1* 100)) ; -> 200, does what we
expect
(with-bindings-from-map wrong-map (+ *v1* 100)) ; -> 110, does the
wrong thing entirely, silently

Anyway, you may want to think about why common-lisp does not have a
with-all-slots macro for use with CLOS objects. It is similar
reasoning which requires you name the slots you want to access when
using with-slots.

samppi

unread,
Aug 11, 2009, 12:12:00 AM8/11/09
to Clojure
I'm also concerned about the implementation, which seems to rely on
what appears to me to be a little magical. An alternative that I was
considering was to just break backwards compatibility and combine all
of the variables into one variable containing a map in a new major
version of my library. But it seems that Mr. Hickey affirms that this
is a stable part of Clojure, so I think for now that I'll go for it.

Meikel Brandmeyer

unread,
Aug 11, 2009, 2:09:23 AM8/11/09
to Clojure
Hi,

On Aug 10, 9:10 pm, jvt <vincent.to...@gmail.com> wrote:

> Be this as it may, you can still get weird run-time behavior from this
> kind of macro in the following situation:
>
> (def *v1* 10)
> (def var-map {:*v1* 100})
> (def wrong-map {:*v2* 10})
>
> (with-bindings-from-map var-map (+ v1 100)) ; -> 200,  does what we
> expect
> (with-bindings-from-map wrong-map (+ v1 100)) ; -> 110,  does the
> wrong thing entirely, silently

The map actually takes the Var itself as key. (def wrong-map {#'*v2*
10})
already blows up, if *v2* does not exist. If it does exist, we are in
the
same situation you have when the key is not present in the map.

But in this situation we are all the time: (:some-key a-map). You type
by accident (:sone-key a-map) and maybe don't notice it until strange
things happen, because the value of ":some-key" is suddenly nil.

If you are concerned about this, you can always add your function
on top of the proposed low-level API to add all safety-checks needed.
While at the moment this is not possible at all...

> Plus, this doesn't depend on a how the compiler implements or
> optimizes names.  It could be that in the future clojure will want to
> use a different representation internally than maps for symbol->value
> translation.

No. It is already independent of the Compiler. The map is
just used to transport the mapping to the Compiler. What
he does internally with the map is completely independent
of that fact. He may use the map directly or do some further
magic to it.

The only hacky point is that Var/pushThreadBindings is not
a public API up-to-now. But Rich already stated his wish to
slate this into a Clojure API via push-thread-bindings.

Sincerely
Meikel
Reply all
Reply to author
Forward
0 new messages