This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-02-11
Channels
- # announcements (1)
- # beginners (67)
- # calva (4)
- # cider (6)
- # clj-kondo (26)
- # clojure (61)
- # clojure-belgium (2)
- # clojure-sweden (1)
- # clojurescript (12)
- # community-development (27)
- # cursive (2)
- # datascript (4)
- # datomic (20)
- # emacs (4)
- # funcool (1)
- # graphql (11)
- # honeysql (3)
- # malli (15)
- # membrane (6)
- # nbb (4)
- # nextjournal (7)
- # pathom (8)
- # polylith (7)
- # rdf (1)
- # re-frame (1)
- # releases (2)
- # shadow-cljs (42)
- # specter (3)
- # tools-deps (25)
- # xtdb (17)
Compojure still works fine, but depending on your needs you might want to look at reitit
I like Pedestal too - very interesting async options in Pedestal, and run-time management of interceptors. Pedestal embraces the Ring spec for requests and responses.
I'm asking because I'm trying to decide whether to implement ring in my clojure-like language, trying to see what could I port then
@UJEK1RGJ0 I've implemented "almost Ring" in another language (completely nothing like Clojure) and it was an interesting exercise that taught me a lot about Ring (as well as the tradeoffs typically made in Language X around HTTP request/response paradigms and web apps in general). I'd say, yes, implement Ring in your Clojure-like language as a learning experience and see what you think of the result.
(ns server2
'http)
(defn handler [req]
(println req)
{:status 200
:body "ok"})
(http/serve2 handler ":7077")
added this:
(defn response [x]
{:status 200 :body x})
(defn wrap-json [handler]
(fn [req]
(-> (if (empty? (:body req))
(assoc req :body nil)
(update req :body read-json {:keywords? true}))
handler
(assoc-in [:headers "Content-Type"] "application/json")
(update :body write-json))))
and I'm already serving APIs ;DIs there a more semantic way to parse strings with a comma as decimal separator instead of a dot?
(parse-double "12,135")
returns nil
, and (parse-double "12.135")
returns 12.135
of type java.lang.Double
I checked if parse-double
accepts a locale, but it doesn't. I tried with clojure.edn/read-string
but that returns 12
when there is a comma as separator.
So for now I do:
(. (java.text.NumberFormat/getInstance (java.util.Locale. "nl" "NL")) parse "12,135")
or (. (java.text.NumberFormat/getInstance (java.util.Locale/FRANCE)) parse "12,135")
whichs returns 12.135
of type java.lang.Double
It just feels like I'm taking the long way around for this š
ps. I know I can shorten the java methods by importing their classes etc. This is as example :)
I'd say the locale l10n stuff is the right approach, especially when you know the locale for sure and know that the format adheres to it.
Not sure whether cases like 10 000,00
are handled by it though, but very well might be.
you can also skip the locales and set whichever separators you want
user=> (let [fmt (DecimalFormat. "#,###.#"
(doto (DecimalFormatSymbols.)
(.setDecimalSeparator \,)
(.setGroupingSeparator \.)))]
(.parse fmt "3.140,121543"))
3140.121543
user=> (let [fmt (DecimalFormat. "#,###.#"
(doto (DecimalFormatSymbols.)
(.setDecimalSeparator \^)
(.setGroupingSeparator \*)))]
(.parse fmt "23*451^34"))
23451.34
For mixing clojure and java, is there any good resources for reloading/watching java classes? I've looked at ztellman/virgil
, but it doesn't quite seem to work anymore š...
This seems to work, but for some reason it doesn't work unless it's done in the context of the REPL: https://groups.google.com/g/clojure/c/Qs6QbaOj5Dg
A few options, depending on your exact needs: ā¢ In my experience, Virgil requires minimal changes to make it work in a modern ecosystem - perhaps, there's even a fork with those changes ā¢ https://github.com/athos/JiSE ā¢ https://github.com/jgpc42/insn ā¢ https://github.com/tailrecursion/javastar Related but not exactly for hot code reloading: ā¢ https://github.com/EwenG/badigeon ā¢ https://github.com/IGJoshua/americano ā¢ https://www.clojure.org/guides/tools_build#_mixed_java_clojure_build
Ok, thanks! That's interesting, I'll check the forks as you mentioned, I didn't think to look earlier as the issues I've been having are basically IllegalAccessException
which I thought were due to changes in the newer JDK's. I'm running 17 for example.
Example:
java.lang.IllegalAccessException: class virgil.compile$get_java_compiler cannot access class com.sun.tools.javac.api.JavacTool (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.api to unnamed module @c166112
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:420)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:709)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:493)
at java.base/java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:128)
at java.base/jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:306)
at java.base/java.lang.Class.newInstance(Class.java:684)
at virgil.compile$get_java_compiler.invokeStatic(compile.clj:101)
I'm using virgil but I force the most recent version of asm in my dependencies (lein-virgil doesn't work though because it overwrites the version of the asm lib)
I've used Virgil not via a Lein plugin but directly from sources. And I had to do this: https://github.com/ztellman/virgil/issues/35#issuecomment-1153280680
So when you say your project
do you mean you added asm to your project deps or to virgil's, I've tried the latter, but not the former yet.
Ok, interesting! Just to check which JVM were you running? I'm using Java 19.0.1 OpenJDK 64-Bit Server VM
.
I've just tried adding [org.ow2.asm/asm "9.4"]
and evaling the below:
(let [file (io/file (System/getProperty "java.home"))
file (if (.equalsIgnoreCase (.getName file) "jre")
(.getParentFile file)
file)
file (io/file file "lib" "tools.jar")
urls (into-array URL [(io/as-url file)])
cl (URLClassLoader/newInstance urls)
compiler-class (Class/forName "com.sun.tools.javac.api.JavacTool" false cl)]
(-> compiler-class
(.newInstance)))
And it seems to still throw an access violation:
#error{:cause "class pankration._scratch.perf.reloader$eval10168 cannot access class com.sun.tools.javac.api.JavacTool (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.api to unnamed module @22e97ef1",
:via [{:type java.lang.IllegalAccessException,
:message "class pankration._scratch.perf.reloader$eval10168 cannot access class com.sun.tools.javac.api.JavacTool (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.api to unnamed module @22e97ef1",
:at [jdk.internal.reflect.Reflection newIllegalAccessException "Reflection.java" 420]}],
:trace [[jdk.internal.reflect.Reflection newIllegalAccessException "Reflection.java" 420]
[java.lang.reflect.AccessibleObject checkAccess "AccessibleObject.java" 709]
[java.lang.reflect.Constructor newInstanceWithCaller "Constructor.java" 493]
[java.lang.reflect.ReflectAccess newInstance "ReflectAccess.java" 128]
[jdk.internal.reflect.ReflectionFactory newInstance "ReflectionFactory.java" 306]
[java.lang.Class newInstance "Class.java" 684]
[pankration._scratch.perf.reloader$eval10168 invokeStatic "reloader.clj" 10]
[pankration._scratch.perf.reloader$eval10168 invoke "reloader.clj" 21]
[clojure.lang.Compiler eval "Compiler.java" 7194]
[clojure.lang.Compiler eval "Compiler.java" 7149]
[clojure.core$eval invokeStatic "core.clj" 3215]
[clojure.core$eval invoke "core.clj" 3211]
[nrepl.middleware.interruptible_eval$evaluate$fn__9074$fn__9075 invoke "interruptible_eval.clj" 87]
[clojure.lang.AFn applyToHelper "AFn.java" 152]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.core$apply invokeStatic "core.clj" 667]
[clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1990]
[clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1990]
[clojure.lang.RestFn invoke "RestFn.java" 425]
[nrepl.middleware.interruptible_eval$evaluate$fn__9074 invoke "interruptible_eval.clj" 87]
[clojure.main$repl$read_eval_print__9206$fn__9209 invoke "main.clj" 437]
[clojure.main$repl$read_eval_print__9206 invoke "main.clj" 437]
[clojure.main$repl$fn__9215 invoke "main.clj" 458]
[clojure.main$repl invokeStatic "main.clj" 458]
[clojure.main$repl doInvoke "main.clj" 368]
[clojure.lang.RestFn invoke "RestFn.java" 1523]
[nrepl.middleware.interruptible_eval$evaluate invokeStatic "interruptible_eval.clj" 84]
[nrepl.middleware.interruptible_eval$evaluate invoke "interruptible_eval.clj" 56]
[nrepl.middleware.interruptible_eval$interruptible_eval$fn__9107$fn__9111
invoke
"interruptible_eval.clj"
152]
[clojure.lang.AFn run "AFn.java" 22]
[nrepl.middleware.session$session_exec$main_loop__9177$fn__9181 invoke "session.clj" 218]
[nrepl.middleware.session$session_exec$main_loop__9177 invoke "session.clj" 217]
[clojure.lang.AFn run "AFn.java" 22]
[java.lang.Thread run "Thread.java" 1589]]}
Oh, actually my bad - I ended up implementing Virgil-like stuff on my own there, heh. Which ended up being much simpler for my needs, around 50 loc.
No but it's basically https://github.com/tailrecursion/javastar but with some bits stripped out to reduce the amount of dependencies, IIRC.
Thank you! I'll give it a try and let you know how it goes š... I'm assuming you would want to know this isn't going to be a problem if you decide to update to a newer jvm!
Hey @U2FRKM4TW, so the above process seems to be working, except for one thing, when it's run in the repl, it appears to compile correctly, but when I modify the file and run it it appears to not load the new definition, still running the old one. Renaming the class to a new name seems to work, so perhaps there's a cache somewhere that needs to be invalidated?
(do
(compile-java "game.perf.Sim" (slurp "src/java/game/perf/Sim.java"))
(.tick (game.perf.Sim.) 0.11))
If I change the print inside tick
from:
System.out.println("tick " + deltaT + " time\n")
to
System.out.println("tick tock " + deltaT + " time\n")
It still prints out tick 0.11 time
Renaming the class to Sim2
+ tweaking the class source + constructor and using (compile-java "game.perf.Sim2" (slurp "src/java/game/perf/Sim.java"))
works
Ah yeah, I see the same behavior in that project. Looks like I haven't gotten to fixing that.
Seems like instead of loadClass
, we should be using defineClass
.
And I don't rely on import
at all - after loading the class, I just refer to it by its FQN. Otherwise, the namespace would fail to load for the very first time when the class is not yet loaded.
So the classloader has a .loadClass
so perhaps there's an unload? I've be back in a sec, getting called away.
I don't really have a throughput to play with it right now, but if you find a way to make it realoadable I'd be curious to know.
Yep, this is the problem!
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
Hmm, not the whole story, source code is compiled inside the .getTask
, it's an interface so tricky to see what it does...
Ok, this may be viable? https://stackoverflow.com/a/66390054 Not super sure how to do these sorts of overrides in clojure...