In terms of real-world solutions, I'd suggest that this sort of
compilation circularity indicates the need for a refactoring or two to
cut that knot. Also, you can use maven modules in order to gain more
control (IMO, in a more comprehensible way to future maintainers
compared to complicated hand-configured interleaved compilation
executions).
If the dependencies aren't circular, and you just need to compile the
clojure code before all of your java code, you can certainly bind
clojure:compile to a different phase that comes before 'compile' --
'process-resources', perhaps. That's a *slight* wart in your pom, but
an easy solution. Again, you can use maven modules to have more
explicit control over the dependencies between different codebases
within the same project.
Cheers,
- Chas
Maven modules are the "correct" solution, but more complicated.
-S
> So really, I think the ideal "correct" solution (not existing yet)
> would be to plug the clojure compiler in the java compiler, creating
> in-memory appropriate stubs for types, records, genclasses and
> geninterfaces in the compiler model.
Of course, this is decidedly nontrivial -- but the integration would
actually go in the other direction. I looked at what this would
entail a year or so ago, and quickly came to the conclusion that the
way the groovy folk do it (IIRC, having groovyc be the "lead" compiler
and call out to javac when necessary, providing classfile stubs of
what will eventually be compiled from groovyland in order to satisfy
potential Java code dependencies) is the only "sane" approach.
I'm not sure that this is a common enough situation to warrant the
individual or community effort that would be required to make the
above a reality in Clojure (especially given a variety of project
configuration and refactoring workarounds). That might change, but I
cringe at the thought of even scoping out the work required.
Cheers,
- Chas
2010/6/6 Chas Emerick <ceme...@snowtide.com>:
>
> On Jun 3, 2010, at 9:42 AM, Laurent PETIT wrote:
>
>> So really, I think the ideal "correct" solution (not existing yet) would
>> be to plug the clojure compiler in the java compiler, creating in-memory
>> appropriate stubs for types, records, genclasses and geninterfaces in the
>> compiler model.
>
> Of course, this is decidedly nontrivial -- but the integration would
> actually go in the other direction. I looked at what this would entail a
> year or so ago, and quickly came to the conclusion that the way the groovy
> folk do it (IIRC, having groovyc be the "lead" compiler and call out to
> javac when necessary, providing classfile stubs of what will eventually be
> compiled from groovyland in order to satisfy potential Java code
> dependencies) is the only "sane" approach.
It could work in most cases, but I fear not in the general case.
Because Clojure has macros. So
* a pure static analysis of the code for deriving all possible stub
is not possible in the general case without running the macros
* and running the macros requires having everything evaled before
the macros being "in good shape". So if some of your clojure macros
have a dependency on a java class which in the call stack will run
directly or indirectly clojure code, chess mat ! :'(
Groovy AFAIK does not have this problem, so the groovy stubs -> javac
-> groovyc sequence should work in all cases.
Now, I know almost nothing about the possibilities to plug behaviour
in javac. It may well be that there's no possibility at all ? :-(
> I'm not sure that this is a common enough situation to warrant the
> individual or community effort that would be required to make the above a
> reality in Clojure (especially given a variety of project configuration and
> refactoring workarounds). That might change, but I cringe at the thought of
> even scoping out the work required.
Yes, it's daunting. Living with that for the moment, and lobbying
collaboratively with other "JVM dynamic languages" to have javac
enhanced with pluggable compiler extensions could be a way to follow ?
> It could work in most cases, but I fear not in the general case.
...
Yes, good point.
>> I'm not sure that this is a common enough situation to warrant the
>> individual or community effort that would be required to make the
>> above a
>> reality in Clojure (especially given a variety of project
>> configuration and
>> refactoring workarounds). That might change, but I cringe at the
>> thought of
>> even scoping out the work required.
>
> Yes, it's daunting. Living with that for the moment, and lobbying
> collaboratively with other "JVM dynamic languages" to have javac
> enhanced with pluggable compiler extensions could be a way to follow ?
Well, javac is already pluggable -- javadoc is a javac "extension",
for example. But the APIs involved are all extraordinarily
complicated and fundamentally undocumented AFAIK.
- Chas
Does that mean that it's not even standardized ? (That is it would
work for Oracle/Sun's javac, but not IBM's , etc. ?)
2010/6/7 Jason Smith <ja...@lilypepper.com>:
> I've worked with both the newer annotation APIs that plug into Javac and
> with extending JavaDoc via their APIs. They don't solve this problem, or
> even come close. Or in other words, "There is no possibility at all."
>
> Working on GMaven for a while, I've had ample time to think about this
> problem, and I have to agree that the only sane way - at present - is the
> Groovy way. This is a general problem for all languages running on the JVM,
> so Sun may eventually come up with a general solution. Until then, the
> cleanest solution I've seen is cross-compilation using stubs, and treating
> Java as the least-common-denominator language.
>
> ***I see what you are saying about the macros.*** Can you give a more
> concrete example of the macro problem? I am thinking about it, and all I
> can come up with is edge cases (where the macro needs the Java class to
> evaluate). Since a macro is code generating code, I am not sure that case
> will come up often. How often will I write a Java class to assist me in a
> macro (probably never, for me)? Why not just use Clojure? Theoretically,
> mathematically it is a problem. Pragmatically speaking, it should be rare.
Well, not sure either it's just a theoretical problem or not. And for
sure having stubs which would clearly solve 90% of the problem would
be great.
My fear was coming from the point that maybe, one of the real selling
points of clojure - macros, e.g. "throw your androMDA cartridges away"
:-) -, when used for real, would make this not work anymore.
I can see 2 points where macros could make this more difficult:
a) macros assisting in removing boiler plate, and, in the process,
ending up in generating bigs (do ...) with genclass, types, protocols,
interfaces definitions. This sole point would imply creating stubs not
only by doing static clojure code analysis, but also dynamic code
evaluation. If there is a cycle at one point between java and clojure
code, this will become problematic to manage.
b) macros themselves using java classes from the project. Maybe, as
you said, not as problematic as point a), but if the macros don't use
java classes but just require them to be on the classpath if e.g. the
macros use gen-class to derive a java class or interface of the
project, then we're back into trouble.
Here are some ideas of scenario which involve points a), b) or both:
* imagine one wants to leverage the ability to easily create
immutable datastructures, usable from java. One could then decide to
create the problem domain concepts with java interfaces, and then let
clojure macroification generate the types for the interfaces. This
implies a cycle, macros, and would be problematic.
* another example : one uses the superb web framework "YAWF" (Yet
Another Web Framework) and has written a splendid abstract class for
his project, namely AbstractMyProjectYAWFController. Now he wants to
leverage clojure and wants it to remove some boilerplate and wants to
write a macro for generating AbstractMyProjectYAWFController
subclasses from declarative clojure datastructures. And of course he
wants to be able to instanciate those subclasses from his java code
(or hopefully from his AOC container). Here again, point b) will
bother us. The defined macro, when called, will look like
(defcontroller MyAwesomeUserLoginController [:splendidActionHook
[:login :password*] ....). And now try to understand from this code
that you must generate a stub fro MyAwesomeUserLoginController,
deriving from AbstractMyProjectYAWFController, with an additional
public YAWFControllerResultAbstraction splendidActionHook(String
login, String[] password) :-( :-(
Does this make sense ?
--
Laurent
Am 07.06.2010 um 23:48 schrieb Laurent PETIT:
> a) macros assisting in removing boiler plate, and, in the process,
> ending up in generating bigs (do ...) with genclass, types, protocols,
> interfaces definitions. This sole point would imply creating stubs not
> only by doing static clojure code analysis, but also dynamic code
> evaluation. If there is a cycle at one point between java and clojure
> code, this will become problematic to manage.
A report from the trenches: VimClojure - using Nailgun - had
to generate a classes for the different commands, which were
then invoked by the nailgun server. This was abstracted away
by a macro, basically wrapping everything in call which basically
looked like a defn in the end.
This structure was now rewritten to work differently (a central
nail written in Java dispatching to normal clojure functions),
but it worked like charm and I wouldn't dismiss this approach
as too exotic.
Sincerely
Meikel