On Fri, Jan 2, 2009 at 1:30 AM, Tom Faulhaber <[email protected]> wrote:
>
> (This is sort of a follow-up to this thread from last July:
> http://groups.google.com/group/clojure/browse_thread/thread/7f5cf3e78954b81d/aae7f082c51337c9?lnk=gst&q=proxy#aae7f082c51337c9.)
>
> Recently, I've been building a version of java.io.Writer
> that knows what the current column is on the output so it
> can handle tabs, etc.
>
> It would be pretty easy to do this with AOT, :genclass,
> etc. and just have a ref to the column number in my state,
> but life seems easier if you don't have to use AOT, so I
> considered doing it with a proxy.
>
> However, proxies *only* support the methods provided by
> their super- classes. They do this so that the proxy class
> can be compiled once, making proxy intances and variation
> super-cheap.
I much prefer using proxy over gen-class when at all
possible, and so far I've had a lot of success. For state
especially, closures are usually sufficient. Here's an
example of a proxy that maintains state in a local mutable
object. In this case it's a StringBuilder -- in other cases
a ref might be more appropriate:
(import '(java.io LineNumberReader FileReader PushbackReader))
(with-open [rdr (LineNumberReader. (FileReader. "test.clj"))]
(let [text (StringBuilder.)
pbr (proxy [PushbackReader] [rdr]
(read [] (let [i (proxy-super read)]
(.append text (char i))
i)))]
(read (PushbackReader. pbr))
(str text)))
This is based on code from clojure.contrib.repl-utils.
I know I mentioned this to you in IRC, but I thought I
should bring it up here for the benefit of others. In a lot
of cases this kind of usage is sufficient.
Your objection was, I believe, that you wanted to return the
proxy object from your function, but allow users of it to
access the state. As I suggested at the time, this can be
done by returning a Clojure collection (probably a map)
instead of the bare proxy object, and having the state live
in there. This has all the benefits of Clojure persistent,
as well as allowing for more than a single state field.
This might look something like:
(defn make-my-obj []
(let [my-state (ref 0)
obj (proxy [BaseClass] [] ...)]
{:my-state my-state, :obj obj}))
This would give you sufficient structure to add as much
state as you want. If you need ways to directly manipulate
that state, rather than going through the :obj, you could
add functions to the hash as well that by closing over the
same state could act as methods. Something like:
((:set-my-state my-obj) new-state)
Of course these could be wrapped in regular functions as
well get a more idiomatic api.
Finally, if this is just too clumsy, I would still prefer
gen-interface over gen-class. This would allow you to
declare all the state-manipulation and -access methods you'd
need. Then you could use 'proxy' and close over any state
objects you need, returning the base proxy object.
--Chouser
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---