Yet another "how do I make gen-class work?" thread

357 views
Skip to first unread message

mikel

unread,
Feb 18, 2009, 6:16:26 PM2/18/09
to Clojure
So I'm looking at improving the implementation of generic functions,
preparatory to making the library more generally available. One thing
I ought to do, I think, is make a Java class, GenericFunction, a
sibling to MultFn. GenericFunction's role is basically the same as
MultiFn's, it just has a different approach to dispatch.

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. I haven't tried using gen-class
before, though, and things are going a bit haywire. Maybe someone can
point out where I've gone wrong. Some code and other diagnostic info
is below. Let me know what else I need to provide.

(ns xg.gf)

(gen-class
:name xg.gf.GenericFunction
:extends [clojure.lang.AFn]
)

user> (compile 'xg.gf)

java.lang.ClassNotFoundException: [clojure/lang/AFn] (gf.clj:7)
[Thrown class clojure.lang.Compiler$CompilerException]


user> (. System (getProperty "java.class.path"))

"/usr/local/clojure/trunk/clojure.jar:/usr/local/clojure/clojure-
contrib/trunk/clojure-contrib.jar:...:/Users/mikel/Valise/guild/trunk/
software/clojure"

caeneshluura:clojure mikel$ pwd
/Users/mikel/Valise/guild/trunk/software/clojure

caeneshluura:clojure mikel$ ls
./ xg/
../ classes/

/Users/mikel/Valise/guild/trunk/software/clojure/xg/gf.clj
/Users/mikel/Valise/guild/trunk/software/clojure/classes/xg/gf/

Laurent PETIT

unread,
Feb 18, 2009, 6:57:58 PM2/18/09
to clo...@googlegroups.com
When trying your code, I encounter the same problem ...

... but what is the compiler error telling us ?

[clojure.lang.AFn] is not a class ?

... maybe clojure.lang.AFn is !

Let's try

(gen-class
 :name xg.gf.GenericFunction
 :extends clojure.lang.AFn
 )

(get rid of the square brackets !)

It's sometimes weird how we just want to see what we want to see ! It took me almost 10 minutes to find what was just in front of me ! :-)

--
Laurent

2009/2/19 mikel <mev...@mac.com>



--
Cordialement,

Laurent PETIT

mikel

unread,
Feb 18, 2009, 7:24:57 PM2/18/09
to Clojure


On Feb 18, 5:57 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> When trying your code, I encounter the same problem ...
>
> ... but what is the compiler error telling us ?
>
> [clojure.lang.AFn] is not a class ?
>
> ... maybe clojure.lang.AFn is !
>
> Let's try
>
> (gen-class
>  :name xg.gf.GenericFunction
>  :extends clojure.lang.AFn
>  )
>
> (get rid of the square brackets !)
>
> It's sometimes weird how we just want to see what we want to see ! It took
> me almost 10 minutes to find what was just in front of me ! :-)

Se what's in front of you this time :-)


I remove the brackets and get a different error:

java.io.IOException: No such file or directory (gf.clj:7)
[Thrown class clojure.lang.Compiler$CompilerException]

mikel

unread,
Feb 18, 2009, 7:49:58 PM2/18/09
to Clojure
(I assume there must be something simple I'm not noticing about how to
set up my classpath properly).

Stuart Sierra

unread,
Feb 18, 2009, 10:02:17 PM2/18/09
to Clojure
On Feb 18, 7:49 pm, mikel <mev...@mac.com> wrote:
> (I assume there must be something simple I'm not noticing about how to
> set up my classpath properly).

An attempt to make things clear...

There are two directories involved in gen-class compilation:
1. your sources dir
2. your compiled files dir, which is set in the Java system property
"clojure.compile.path".

The compiled dir must exist; Clojure will not create it.

BOTH dirs must be on the classpath, like this:

java \
-cp clojure.jar:/your/classes/dir:/your/sources/dir \
-Dclojure.compile.path=/your/classes/dir \
clojure.main

Since your namespace is "xg.gf", there should be a file named
/your/sources/dir/xg/gf.clj

However, the "standard" way to use gen-class is to have a namespace
with the same name as the resulting Java class, like this:

In the file "/your/sources/dir/xg/gf/GenericFunction.clj":
(ns xg.gf.GenericFunction
(:gen-class :extends clojure.lang.AFn))

Now (compile 'xg.gf.GenericFunction) will generate a Java class named
"xg.gf.GenericFunction". When THAT class is loaded later on, it will
use the functions defined in the Clojure namespace
"xg.gf.GenericFunction".

It's important to remember that gen-class really only generates a stub
class, which dynamically loads a Clojure namespace (from source or
compiled) and uses the definitions from that namespace.

-Stuart Sierra

mikel

unread,
Feb 18, 2009, 10:58:47 PM2/18/09
to Clojure


On Feb 18, 9:02 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
> On Feb 18, 7:49 pm, mikel <mev...@mac.com> wrote:
>
> > (I assume there must be something simple I'm not noticing about how to
> > set up my classpath properly).
>
> An attempt to make things clear...
>
> There are two directories involved in gen-class compilation:
> 1. your sources dir
> 2. your compiled files dir, which is set in the Java system property
> "clojure.compile.path".

This was the issue; setting clojure.compile.path made everything
wonderful; thanks!

Chouser

unread,
Feb 19, 2009, 10:21:26 AM2/19/09
to clo...@googlegroups.com
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

--Chouser

mikel

unread,
Feb 19, 2009, 11:34:42 AM2/19/09
to Clojure
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.



Chouser

unread,
Feb 19, 2009, 12:07:20 PM2/19/09
to clo...@googlegroups.com
On Thu, Feb 19, 2009 at 11:34 AM, mikel <mev...@mac.com> wrote:
>
> 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.

I'm not your target audience for that question, but you bring up some
other interesting questions as well...

> 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.

On this point, would it help if you could provide a custom print for
the object? This idea has come up a couple times now, and I have a
toy implementation that seems indicate it could be done cleanly. It
would allow your make-generic-function to return an object that would
print uniquely, with whatever combination of name and closed-over data
you want, independant of the specific proxy class that its an instance
of. Of course calling 'class' on it would still show the goofy proxy
class name, but I wouldn't expect that to be a big problem.

> 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.

You can close over an atom or ref to get your mutable state. This is
a mess and by itself useless, but maybe it helps show what I mean:

(defn make-thing [a b]
(let [state (atom {})]
(proxy [clojure.lang.AFn clojure.lang.IDeref] []
(deref [] state)
(invoke
([] "no arg")
([x] "one arg")))))

(def thing (make-thing 1 2))

Now you can call the thing with zero or one args:

user=> (thing 1)
"one arg"

You can also write functions that mutate it's state. A add-method
function could be defined to do something like:

(swap! @thing assoc :A 1)

That new value can be accessed from any of the invoke bodies, or
anywhere else, actually:

user=> thing
#<AFn$IDeref@2a6ff: #<Atom@21d23b: {:A 1}>>

That prints ugly indeed. Rich, do you have any interest in per-object
custom print fns, hanging off meta-data?

> I'd be interested in opinions about what the best approach is and why.

I can't say with confidence what the best approach is, I just know
that much prefer the dynamic nature of proxy and when possible would
rather use it. Perhaps what I outlined above is too convoluted for
your taste, and I think that would be a reasonable response. I mostly
want to make sure proxy is not overlooked.

--Chouser

mikel

unread,
Feb 19, 2009, 4:51:22 PM2/19/09
to Clojure


On Feb 19, 11:07 am, Chouser <chou...@gmail.com> wrote:
> On Thu, Feb 19, 2009 at 11:34 AM, mikel <mev...@mac.com> wrote:
>
> > 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.
>
> I'm not your target audience for that question, but you bring up some
> other interesting questions as well...
>
> > 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.
>
> On this point, would it help if you could provide a custom print for
> the object?

If I can implement a custom printing function that gets called on
generic functions, that presupposes that I have some way to tell that
an object is a generic function. So, yes, if I solve the problem, then
the problem is solved. :-)

>  This idea has come up a couple times now, and I have a
> toy implementation that seems indicate it could be done cleanly.  It
> would allow your make-generic-function to return an object that would
> print uniquely, with whatever combination of name and closed-over data
> you want, independant of the specific proxy class that its an instance
> of.  Of course calling 'class' on it would still show the goofy proxy
> class name, but I wouldn't expect that to be a big problem.
>
> > 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.
>
> You can close over an atom or ref to get your mutable state.

Sure; that's how generic functions are implemented now, but overriding
deref is a nice idea that eliminates the need for sentinel values. It
provides a nice way to implement add-method, remove-method, and
generic-function? without resorting to sentinel values. I think the
only amenity that leaves unaccounted-for is the minor matter of some
sort of fastidiousness about having GenericFunction as a peer of
MultiFn, but truthfully, I'm not sure there's really any reason to
want that.

Thanks for the idea.

Chouser

unread,
Feb 20, 2009, 12:05:11 PM2/20/09
to clo...@googlegroups.com
On Thu, Feb 19, 2009 at 4:51 PM, mikel <mev...@mac.com> wrote:
>
> If I can implement a custom printing function that gets called on
> generic functions, that presupposes that I have some way to tell that
> an object is a generic function. So, yes, if I solve the problem, then
> the problem is solved. :-)

Hm, perhaps I was unclear. I've been playing with a little patch to
allow you to attach a print fn to anything that can hold metadata:

(defmacro do-writes [w & exprs]
(let [wsym (gensym)]
`(let [~wsym ~(with-meta w {:tag 'java.io.Writer})]
~@(for [e exprs]
`(.write ~wsym (str ~e))))))

user=> #^{:custom-print #(do-writes %2 "#<foo: " (:a %) ">")} {:a 1 :b 2}
#<foo: 1>

To extend further my already over-extended example from earlier:

(import '(clojure.lang IFn IDeref IMeta))

(defn make-thing [a b]
(let [state (atom {})]

(proxy [AFn IDeref IMeta]
[{:custom-print
#(do-writes %2 "#<thing: a " a ", b " b ": " @@% ">")}]


(deref [] state)
(invoke
([] "no arg")
([x] "one arg")))))

(def thing (make-thing 1 2))

(swap! @thing assoc :z 1)

user=> thing
#<thing: a 1, b 2: {:z 1}>

All that and no need for an AOT compilation step first.

I've attached a patch to support the :custom-print metadata, in case
anyone wants to play with it.

> I think the only amenity that leaves unaccounted-for is the minor
> matter of some sort of fastidiousness about having GenericFunction
> as a peer of MultiFn, but truthfully, I'm not sure there's really
> any reason to want that.

Both extend AFn, so I think they're siblings even if one of them has a
goofy name.

--Chouser

custom-print.patch
Reply all
Reply to author
Forward
0 new messages