On Feb 19, 9:21 am, Chouser <chou...@gmail.com> wrote:
> On Wed, Feb 18, 2009 at 6:16 PM, mikel <mev...@mac.com> wrote:
>
> > Now, of course I could write GenericFunction in Java, just as MultiFn
> > is written in Java, but I thought it would be nice to use Clojure's
> > Java interop features to do it.
>
> Not to spoil all your gen-class fun, but did you consider using proxy
> instead? Note that even Clojure builtins have been built using proxy
> -- formerly 'atom' and now 'future': http://tinyurl.com/bpjew7
I did consider it, and am still considering. I'd be interested to know
other people's opinions about the best (that is, most comfortable for
users, least out-of-place) way of providing GFs in library code. Using
gen-class is just one option, but it provided a convenient excuse to
learn how to use gen-class.
I also have a file of Java source that implements GenericFunction as a
sibling of MultiFn. One could make a case that this is the most
suitable implementation, because it offers GenericFunction as an
alternative implementation of polymorphic functions alongside MultiFn.
At the moment, that implementation of GenericFunction lives in the
clojure.lang package; perhaps Rich won't like that, since it's not, in
fact, part of the Clojure language. Maybe it ultimately should live in
clojure.contrib, or in some completely different package.
I could certainly implement GFs by making make-generic-function return
an AFn proxy. A disadvantage of doing that (and of reoresenting GFs
as closures, as the first implentation did) is that a generic function
object then has no identity as such; that is, you can't tell from
looking at it that it's a generic function. It's some AFn proxy (or
closure) that might be anything. If you want to know whether it's a
gf, you just have to try using it as one and see what happens.
Another disadvantage is that, like MultiFns, generic functions have
some state. In both cases, the state is a dispatch table. The dispatch
algorithm used is different for the two types of polymoprhic
functions, but both carry around data used by dispatch, and both sort
of want you to be able to update the state (in the case of MultiFn, by
calling defmethod). You kind of want to have an equivalent for generic
functions of defmethod, and of add-method and remove-method.
Generic functions also need to be IFns of some kind, because we want
to apply them like functions. When a generic function gets called, you
want to invoke the dispatch algorithm to find the right methods, order
them by specificity, set up the environment to make next-method work,
and then apply the most specific method to the actual arguments.
The first way I did this was to stuff the state into a lexical
environment and close over that environment when creating the GF. That
works just fine, but has the disadvantage that the only way to reach
the state is by calling the GF. That means that, in addition to its
normal behavior on application, the GF needs to check its arguments to
see if they are the sentinel values that indicate we are not actually
calling the GF, we are fetching or updating the dispatch table. That,
of course, partitions the world of possible argument values into two
parts: things that are sentinel values for dispatch-table operations
and everything else. Also, the implementation code is just slightly
brain-bending; I had no trouble implementing it, but it's the sort of
code that gives you the queasy feeling that you are going to need to
stick a few paragraphs of explanation into comments so that later you
can figure out what the heck you were thinking when you wrote it.
Okay, how else could I implement GFs such that they can be applied
like functions, but also support an add-method, remove-method API?
Well, I could make a grand galactic table of generic functions and
store the dispatch tables on it. I prefer to keep data values
anonymous whenever possible, so using GF names as keys doesn't appeal
to me. So maybe, then, I use the GF objects themselves as the keys in
the galactic table, mapping each one to its dispatch table. That means
that if I call define-generic-function (or whatever), it creates a GF,
interns it in the grand galactic table, and returns a value that can
then be bound to some Var. Well and good, but of course if I call it
again with the same name, the value of the Var gets updated, but the
old GF hangs around forever in the galactic table. I guess I'd rather
use a solution in which each GF is self-contained and carries its
state around with it (and the state gets GCed along with the GF).
That leads me back around to using gen-class or writing
GenericFunction in Java. I can use gen-class to get a suitably-named
class of suitable pedigree. In its favor, that solution permits me to
implement the dispatch algorithm in Clojure, which is a lot easier
than implementing it in Java. On the other hand, it means that I need
to stick some mutable state in an array or something, so that I can
implement add-method and remove-method for the generated class. At
that point, it starts to look like I'm just jumping through arbitrary
hoops, and maybe I should just write GenericFunction in Java after
all.
I'd be interested in opinions about what the best approach is and why.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---