Is it possible to use gen-class to create a class without compiling it? I'm following a tutorial for orbit and am getting stuck here: https://github.com/orbit/orbit/wiki/Getting-Started%3A-Hello-World#actor-implementation.
I can use gen-interface to make an interface:
(import cloud.orbit.actors.Actor)
(import cloud.orbit.actors.runtime.AbstractActor)
(import cloud.orbit.concurrent.Task)
(gen-interface
:name example.Hello
:extends [cloud.orbit.actors.Actor]
:methods [[sayHello [String] cloud.orbit.concurrent.Task]])
but gen-class doesn't work and I'm getting stuck
(gen-class
:name example.HelloActor
:extends [cloud.orbit.actors.runtime.AbstractActor]
:implements [example.Hello]
:methods [[sayHello [String] cloud.orbit.concurrent.Task]])
In the repl you could also do:
(def original-compile-files *compile-files*)
(alter-var-root #'*compile-files* (constantly true))
(gen-class
:name example.HelloActor
:extends [cloud.orbit.actors.runtime.AbstractActor]
:implements [example.Hello]
:methods [[sayHello [String] cloud.orbit.concurrent.Task]])
(alter-var-root #'*compile-files* (constantly original-compile-files))
Then the example.HelloActor class should be available on the repl classpath immediately.
This has implications of course in that you have to alter the var root, which is not ideal. In this example I just set it back to its default of false when done.
Have you seen this flowchart?
https://github.com/cemerick/clojure-type-selection-flowchart
I don't have much experience with gen-class, proxy, et al, but you should be sure to review all of the options under "Proxy" on the Clojure Cheatsheet.
The short answer is: no, you cannot use gen-class without compiling.
The more interesting answer is: yes, you can use gen-class from the repl if your repl has the right settings.
Here is a working example for Boot: https://gist.github.com/jeroenvandijk/8187413d24433545eeb9579538a903f7#file-repl_compile-clj-L39-L52
Related
I'd like to construct a ClojureScript macro (executed/compiled via Clojure) that informs it's construction of a return form based on the static, compile-time metadata of a ClojureScript var argument.
I understand it is possible to access compile-time, static var metadata from ClojureScript code (using (meta (var varsym)); see this post). But is this data accessible to the compilation process in such a way that we could access it from a macro?
Here's a rough sketch of what I'd like to do (and the question is really how you'd write get-meta-for-varsym below):
;; executed/compiled in clj, targeting cljs
(defmacro themacro
[varsym & args]
(let [var-meta (get-meta-for-varsym varsym)
return-form (compile-return-form-from-metadata var-meta args)]
return-form))
For this you have to use the Clojurescript analyzer:
(ns your-macros
(:require [cljs.analyzer :as cljs]))
(defmacro var-data
[sym]
(cljs/resolve-var &env sym))
Then in your clojurescript file:
(ns your-cljs)
(def ^{:foo :bar} xxy {})
(var-data xxy)
The meta data will be in :meta key of the map.
Let's say I create a new Leiningen project (lein new app example) and add some code in example/src/example/core.clj that makes use of :gen-class:
(ns example.core
(:gen-class :extends javafx.application.Application))
(defn -start [this stage]
(.show stage))
(defn -main [& args]
(javafx.application.Application/launch example.core args))
If I then create a JAR (lein uberjar) and run it, everything works fine. However, if I instead try to run my app directly (lein run), I get a ClassNotFoundException. In addition, if I open a REPL (lein repl), I first get the same error as before, but after running this code:
(compile 'example.core)
the error no longer appears in lein run or lein repl.
Could someone please explain to me what exactly is going on here, and how I can run my app directly without needing to manually compile my code from a REPL?
Edit: After fooling around a bit more, I found that the solution to this problem is to add
:aot [example.core]
to project.clj. (Thanks #Mars!) I'm still confused, though, because I had previously tried simply removing ^:skip-aot, which (according to the docs) should work:
This will be AOT compiled by default; to disable this, attach
^:skip-aot metadata to the namespace symbol.
But it doesn't. Why?
Another edit (if I should split any of this into a separate question, let me know and I'll do so): I've been playing with hyphens (lein new app my-example), and weird stuff has been happening. This doesn't work:
(ns my-example.core
(:gen-class :extends javafx.application.Application))
;; ...
(defn -main [& args]
(javafx.application.Application/launch my-example.core args))
But this does:
(ns my-example.core
(:gen-class
:name my-example.Core
:extends javafx.application.Application))
;; ...
(defn -main [& args]
(javafx.application.Application/launch my-example.Core args))
So my class name can either start with a lowercase letter (example.core) or contain a hyphen (my-example.Core), but not both? I really don't get it.
And finally, lein uberjar fails on that final example (with the explicit :name), because
Warning: The Main-Class specified does not exist within the jar. It may not be executable as expected. A gen-class directive may be missing in the namespace which contains the main method.
As far as I can tell, the only way to fix that is to split the Application subclass into a separate namespace. Is there another way?
Agreed with #Mars, the problem is that lein run does not AOT the example.core namespace. The default Leiningen template made the example.core non AOT:
(defproject example "0.1.0-SNAPSHOT"
...
:main ^:skip-aot example.core
...)
My best guess is that you could define your app using defrecord and use that as a class instead of the namespace.
I'm writing a small debugging library and I would like to let users choose how to display data structures. I was imagining that users could require it in this kind of way:
(ns some-user-namespace
(:require
[clojure.pprint]
[my.library :with-args {print-fn clojure.pprint/pprint}]))
Is something like this possible, and if not, how can I solve this problem?
It's not possible to do it this way. If you really to offer this kind of setup, you could provide a configuration function to be called by the user after the import:
(ns some-namespace
(:require [my.library]))
(my.library/print-with! clojure.pprint/pprint)
Ending function name with ! is an idiomatic way of indicating that it's causing some side effects.
In your library it could look like:
(ns my.library)
(def config (atom {:printer println}))
(defn print-with! [new-printer]
(swap! config assoc :printer new-printer))
(defn my-lib-print [foo]
((:printer #config) foo))
EDIT: For a solution that does not require global, mutable state you can use dynamic bindings.
Lib:
(ns my.library)
(def ^:dynamic *printer* println)
(defn my-lib-print [foo]
(*printer* foo))
Usage:
(binding [my.library/*printer* clojure.pprint/pprint]
(my.library/my-lib-print {:hello "World"}))
These are the only two ways for some kind of external, contextual configuration I can think of. The only alternative is pure higher order function:
(defn my-lib-print [printer foo]
(printer foo))
I'm currently reading the Clojure Programming book, and following the examples. However I come across an error.
(defn print-logger
[writer]
#(binding [*out* writer]
(println %)))
(def *out*-logger (print-logger *out*))
(*out*-logger "hello")
Will result in: `Can't dynamically bind non-dynamic var: user/out
I'm very new to Clojure, and don't understand why this happens, especially when I'm following the example :)
The issue is not in the code sample you have.
*out* should refer to clojure.core/*out*, so if you are running this in a repl you might have run something previous, to what you have, like (def *out* something) to create a user/*out*.
Then, when you defined your print-logger function, the (binding [*out* writer] ...) statement would be trying to rebind user/*out* instead of clojure.core/*out*.
You can use ns-unmap to remove user/*out* from your namespace.
(ns-unmap 'user '*out*) ;; => nil
You will also need to define your print-logger function again to recapture the correct clojure.core/*out*.
(ns scratch.fastflip
(:gen-class
:extends java.util.Random
:implements clojure.lang.IFn))
(defn -invoke [^java.util.Random this]
(.next this 1))
Loading the file I get the warning:
;scratch.coin=> Reflection warning, /home/user/scratch/src/scratch/fastflip.clj:8 - call to next can't be resolved.
#'scratch.fastflip/-invoke
Note I want to get rid of the warning via eliminating the reflection, not via setting warning mechanism to false.
If you're on 1.3, this is probably because next takes an int, not a long, and 1 is an int. But are you sure next is what you want to call? If you're doing coin-flipping, I would just use nextInt(2), as next looks like implementation internals.
Edit: Here's syntax you can use to do what you want without a reflection warning.
(ns test-genclass.core
(:gen-class
:extends java.util.Random
:implements [clojure.lang.IFn]
:exposes-methods {next inner}))
(set! *warn-on-reflection* true)
(defn -invoke [^test_genclass.core this]
(.inner this 1))
(defn -main [& args]
((test_genclass.core.)))