I have a function declared as follows:
(defn fn-name [^ClassName c] (.field c))
where ClassName is defined in a previous defrecord in the same package.
When I try to run the function, I get a runtime error:
ClassCastException pkg.ClassName cannot be cast to pkg.ClassName pkg/fn-name (...).
The ^ClassName appears to be used to prevent reflection warnings about .field, and so seems to need to be there. I don't understand why the function call is trying to convert the parameter when it's already of the declared type. Any idea how to either get rid of the error or how to prevent the reflection warnings without the declaration (because removing the declaration seems to remove the runtime error, but has the reflection warning)?
If it's a defrecord use keyword lookup
@fadrian I can't repro that error:
$ clj
Clojure 1.10.1
user=> (set! *warn-on-reflection* true)
true
user=> (defrecord ClassName [field])
user.ClassName
user=> (defn fn-name [c] (.field c))
Reflection warning, NO_SOURCE_PATH:1:19 - reference to field field can't be resolved.
#'user/fn-name
user=> (defn fn-name [^ClassName c] (.field c))
#'user/fn-name
user=> (fn-name (->ClassName 42))
42
user=>
I see the reflection warning (expected). Using the record's class name as a type hint removes the reflection warning (expected). Although as hiredman says, you could just use (:field c)
which would be idiomatic.
Changing the .field to :field worked. Thanks.
I'm running into a problem with type hinting a constructor. Even with the type hints it complains ctor can't be resolved.
the constructor is like public A(B firstParam, C... secondParam)
my call is like (A. ^B b-instance ^C c-instance)
but this doesn't work
i've tried ^[Lfully.qualified.C [c-instance]
too but that didn't work either
you might need something like (A. b-instance (into-array C c-instance))
thanks for the suggestion! i just tried that and it did not resolve the reflection warning. to give concrete sample: i'm trying to instantiate a netty class with this constructor: https://netty.io/4.1/xref/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.html#107 like this:
(Http2ServerUpgradeCodec.
^Http2FrameCodec (.build (Http2FrameCodecBuilder/forServer))
^ChannelHandler handler)
it keeps telling me that call to io.netty.handler.codec.http2.Http2ServerUpgradeCodec ctor can't be resolved.
and fails to actually work
sadly it doesn't go into detail why it can't resolve the constructor (listing the argument types)
the ctor in question is public too
you need to pass (into-array ChannelHandler [])
as a second argument
Java interop doesn’t let you ignore varargs
i've tried both with (into-array ChannelHandler [handler])
and (into-array ChannelHandler handler)
and neither worked
so this doesn’t work?
(Http2ServerUpgradeCodec. (.build (Http2FrameCodecBuilder/forServer)) (into-array ChannelHandler [handler]))
yes it still tells me ctor can't be resolved
then I don’t know
i tried making a static java method that does the same and it resolves the warning
weird stuff
Try this
(Http2ServerUpgradeCodec. (.build (Http2FrameCodecBuilder/forServer)) ^"[Lio.netty.channel.ChannelHandler;" (into-array ChannelHandler [nil]))
(naturally, replace the nil with your handler :-))
(Http2ServerUpgradeCodec. (.build (Http2FrameCodecBuilder/forServer)) ^"[Lio.netty.channel.ChannelHandler;" (into-array ChannelHandler [nil]))
#object[io.netty.handler.codec.http2.Http2ServerUpgradeCodec
"0x2c6ee031"
"io.netty.handler.codec.http2.Http2ServerUpgradeCodec@2c6ee031"]
user=> (Http2ServerUpgradeCodec. (.build (Http2FrameCodecBuilder/forServer)) (into-array ChannelHandler [nil]))Reflection warning, NO_SOURCE_PATH:1:1 - call to io.netty.handler.codec.http2.Http2ServerUpgradeCodec ctor can't be resolved.
#object[io.netty.handler.codec.http2.Http2ServerUpgradeCodec
"0x64a552"
"io.netty.handler.codec.http2.Http2ServerUpgradeCodec@64a552"]
arrgh was it the ampersand?
no
[Lio.netty.channel.ChannelHandler;"
i tried that, but without the ampersand
you mean semicolon?
ampersand = &
sorry, that. at this point i'm confusing words
yeah, missing the semicolon 🙂
that's just SOOO java lol
you saved the day, thanks!"
Is a good idea to run a Clojure application in production by using cli -M -m app.core
directly, or shall we go through a jar anyway?
you're most welcome, the ;
is part of the class format
(or rather field descriptors)
it'd be great if something yelled at me that my type hint is incorrect in that sense
@vincenz.chianese I don't think it really matters as long as you tweak your JVM settings properly via -J-D
. If you do AOT compilation you will have faster startup, if that matters for redeploys.
That would be handy, but no 🙂
https://docs.oracle.com/javase/specs/jvms/se14/html/jvms-4.html
4.3.2. Field Descriptors
also if deps.edn frequently changes, you will have slightly slower startup time due to classpath calculation
@borkdude Thanks for the answer; what JVM settings are you referring to?
For primitives, it's fine, i.e., ^long
, ^double
and so on
but with an array of objects, i.e., into-array
, you have to tell the compiler what's in the array
and for that, you need to use that construct, i.e., "[Ljava.lang.String;"
I learnt the hard way too 🙂
@vincenz.chianese you can look those up via your favorite search engine. Heap memory etc.
Ah ok ok I thought there was something specific for Clojure's startup
I think in most cases both approaches should work well
Keeping deps edn invocations up to date on the server and in local dev needs attention sometimes
(e.g. -A won't invoke main in a couple of months from now)
I am not an expert nor experienced in deploying Clojure apps to a fleet of servers in production, but I believe some people that do prefer to create JAR files and then run them on the servers using something like java <jvm-options> foo.jar
. One reason: It makes the versions of dependencies that are used more repeatable across all servers. If a new version of a dependency is published after your app is deployed on some servers, but before others, or after you have done your testing, but before deployed to the first server, clj
could find the newer version rather than the one you tested with.
Usually that wouldn't be a problem, but why tempt fate?
How? Assuming, there are no snapshots and no overwritten artifacts and that you deploy the same deps.edn
everywhere.
Sometimes people do terrible things like publish a new artifact with the same version
(google actually did this with guava)
Or tools-deps could have a big that causes into resolve the deps differently on one of your servers
Basically dependency resolution and fetching is a complicated process, your deploys will be more reliable of it is not part of the deploy
Ah, that's what I meant by "overwritten artifacts", yeah. Having a bug is also reasonable, thanks.
I do think deploying uberjars leaves a lot to be desired, in particular uberjars can get really large, and end up containing a lot of the some bytes between deploys
the two solutions I've looked at are using binary diffs of uberjars and not building uberjars, but copying dependencies over individually, and deploying a prebuilt file containing a classpath pointing to those dependencies
Have you seen anything promising or even production ready that handles the latter scenario?
build tool wise? no I have some code I was playing with for it (https://gist.github.com/hiredman/d68cafb6aa8cea563c7b77d54f522421), and I suspect it is more or less how datomic ions handle deploys, but I am only basing that on how rich described it in a talk he gave
Thanks!
around here https://youtu.be/thpzXjmYyGk?t=3214 is the point in the rhickey talk that makes me think datomic ions are doing something like that
Oh right - you had this dialog with dominicm before, and I took notes, including your gist and this YT video. :D Dominic also mentioned this thing that doesn't do everything but can at least facilitate such a deployment: https://github.com/juxt/pack.alpha#skinny-jar
Doesn't clojure/cli Clojure CLI tools take more recent versions than are in your deps.edn file if they are published, even if you don't use SNAPSHOT versions?
If so, as I believe is true, that can happen at times unknown to you, the user of the dependency.
it doesn't
the take the most recent thing is only if you depend on a given library twice with different versions
Hmmmm, I asked Alex a related question once, and I thought he said it does, but I might not have been asking the question properly.
So if dep X is used by three different things you depend upon directly, but you do not depend upon X directly, then you should get the minimum version among what those three direct dependencies ask for?
I'm sorry, the maximum, but no later, is what I meant to ask.
yes
OK, yeah, that is definitely more reproducible than taking a later version than the maximum.
This talk gives a good description of the classpath building algorithm. The most relevant part starts around 15 minites. https://www.youtube.com/watch?v=7CM7Ef-dPWQ