thread-local binding interface and bound-fn

57 views
Skip to first unread message

Meikel Brandmeyer

unread,
Aug 8, 2009, 6:07:57 PM8/8/09
to cloju...@googlegroups.com
Hi,

as discussed in:
http://groups.google.com/group/clojure/browse_frm/thread/69367d3981de81d1

I added the clojure side of the interface for the clojure.lang.Var. I
also added a macro called "bound-fn", which returns a function which
restores the bindings in effect when the function was defined before
executing the function body. It uses a helper I called binding*
because it works similar to binding but is a function. I found this to
be useful in other projects. So I thought it may be useful to others
also.

I split the whole thing into two tickets, because I thought, that the
interface thing is pretty simple, while bound-fn might need more
discussion...

Here are the links:
https://www.assembla.com/spaces/clojure/tickets/169-thread-local-bindings-interface
https://www.assembla.com/spaces/clojure/tickets/170-bound-fn-macro

Sincerely
Meikel

Laurent PETIT

unread,
Aug 10, 2009, 7:12:36 AM8/10/09
to cloju...@googlegroups.com
Hi Meikel,

binding*'s docstring says "Acts similar to the binding macro" but it seems to me that its first argument will be a map, whereas binding's first argument must be a (literal) vector ? Maybe a precision for this difference on the doc ?

Also, wouldn't it be interesting to provide a macro whose purpose would be to just encapsulate an existing fn ? This could then allow functions whose purpose is to delegate processing to other threads for technical rather than business reasons (think pmap) to encapsulate the whole thread binding information just before quitting the "business" thread ?

(defn bound-fn*
  "Returns a function, which will install the same bindings in effect as
  in the thread at the time bound-fn* was called. This may be used to define a helper
  function which runs on a different thread, but needs the same bindings
  in place."
  [f]
  (let [bindings (get-thread-bindings)]
     (fn [& args]
       (binding* bindings f args))))

(not tested)

Of course you can then rewrite bound-fn in terms of bound-fn* as well.

Regards,

--
Laurent


2009/8/9 Meikel Brandmeyer <m...@kotka.de>

Meikel Brandmeyer

unread,
Aug 10, 2009, 8:59:46 AM8/10/09
to Clojure Dev
Hi,

On Aug 10, 1:12 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> Also, wouldn't it be interesting to provide a macro whose purpose would be
> to just encapsulate an existing fn ? This could then allow functions whose
> purpose is to delegate processing to other threads for technical rather than
> business reasons (think pmap) to encapsulate the whole thread binding
> information just before quitting the "business" thread ?

*oerk* Swallowing the own pill...

> (defn bound-fn*
> "Returns a function, which will install the same bindings in effect as
> in the thread at the time bound-fn* was called. This may be used to define a helper
> function which runs on a different thread, but needs the same bindings
> in place."
> [f]
> (let [bindings (get-thread-bindings)]
> (fn [& args]
> (binding* bindings f args))))
^ + apply

(defmacro bound-fn
[& fntail]
`(bound-fn* (fn ~@fntail)))

However this would not allow for recursive calls, which happen delayed
as I explained in the comment for the ticket.

Hmm... How about restructuring the whole thing and going away with the
nameing from binding all together? The difference between with-
bindings
and binding would be, that with-bindings takes (maybe at runtime
defined)
map instead of a vector.

(defn with-bindings*
"Takes a map of Var/value pairs. Installs for the given Vars
the associated values as thread-local bindings. Then calls f
with the supplied arguments. Pops the installed bindings
after
f returned. Returns whatever f returns."
[binding-map f & args]
(push-thread-bindings binding-map)
(try
(apply f args)
(finally
(pop-thread-bindings))))

(defmacro with-bindings
"Takes a map of Var/value pairs. Installs for the given Vars
the associated values as thread-local bindings. The executes
body. Pops the installed bindings after body was evaluated.
Returns the value of body."
[binding-map & body]
`(with-bindings* ~binding-map (fn [] ~@body)))

(defn bound-fn*
"Returns a function, which will install the same bindings in
effect as in the thread at the time bound-fn* was called and
then call f with any given arguments. This may be used to
define a helper function which runs on a different thread,
but
needs the same bindings in place."
[f]
(let [bindings (get-thread-bindings)]
(fn [& args]
(apply with-bindings* bindings f args))))

(defmacro bound-fn
"Returns a function defined by the given fntail, which will
install the same bindings in effect as in the thread at the
time bound-fn was called. This may be used to define a
helper
function which runs on a different thread, but needs the
same bindings in place."
[& fntail]
`(bound-fn* (fn ~@fntail)))

Then a seq generating function, relying on some thread-local bindings,
could be written as:

(defn my-hypothetical-seq
[coll]
(let [bindings (get-thread-bindings)
step (fn step [s]
(lazy-seq
(with-bindings bindings
(generate-seq-here-calling-step))))]
(step coll)))

What do you think?

Sincerely
Meikel

Laurent PETIT

unread,
Aug 10, 2009, 10:37:37 AM8/10/09
to cloju...@googlegroups.com


2009/8/10 Meikel Brandmeyer <m...@kotka.de>

Seems good to me.
It's just the with-bindings macro. Now that it accepts a Map, it does not look like other with-open... existing macro which accept literal vectors...

Meikel Brandmeyer

unread,
Aug 10, 2009, 11:23:42 AM8/10/09
to Clojure Dev
Hi,

On Aug 10, 4:37 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:

> Seems good to me.
> It's just the with-bindings macro. Now that it accepts a Map, it does not
> look like other with-open... existing macro which accept literal vectors...

Maybe some other name? "in-dynamic-context", "wrap-bindings"?
I always want to end up with something like with-.....

Sincerely
Meikel

Laurent PETIT

unread,
Aug 10, 2009, 11:32:03 AM8/10/09
to cloju...@googlegroups.com

2009/8/10 Meikel Brandmeyer <m...@kotka.de>

Is it an option to make with-bindings accept either a non literal map object or a literal vector object ?

Meikel Brandmeyer

unread,
Aug 11, 2009, 2:17:26 AM8/11/09
to Clojure Dev
Hi,

On Aug 10, 5:32 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:

> Is it an option to make with-bindings accept either a non literal map object
> or a literal vector object ?

I'm not sure about that either. One could allow
both: a map or a vector. But then... binding is also
inconsistent with let and similar constructs:

(def a 1)
(def b 2)
(binding [a 3
b a]
(+ a b))
=> 4

Sincerely
Meikel

Laurent PETIT

unread,
Aug 11, 2009, 3:14:08 AM8/11/09
to cloju...@googlegroups.com


2009/8/11 Meikel Brandmeyer <m...@kotka.de>


Hi,

On Aug 10, 5:32 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:

> Is it an option to make with-bindings accept either a non literal map object
> or a literal vector object ?

I'm not sure about that either. One could allow
both: a map or a vector. But then... binding is also
inconsistent with let and similar constructs:

Indeed

Reply all
Reply to author
Forward
0 new messages