This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-10-24
Channels
- # aleph (1)
- # babashka (2)
- # beginners (25)
- # calva (5)
- # cider (8)
- # cljdoc (4)
- # clojure (81)
- # clojure-europe (41)
- # clojure-spec (11)
- # clojurescript (7)
- # conjure (1)
- # data-science (1)
- # datomic (25)
- # defnpodcast (3)
- # events (2)
- # figwheel-main (8)
- # fulcro (5)
- # helix (4)
- # hugsql (1)
- # java (2)
- # off-topic (35)
- # onyx (18)
- # pathom (8)
- # rdf (5)
- # re-frame (9)
- # reagent (3)
- # reitit (1)
- # shadow-cljs (39)
- # tools-deps (53)
- # xtdb (23)
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)?
@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.
I'm running into a problem with type hinting a constructor. Even with the type hints it complains ctor can't be resolved.
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)
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]))
(Http2ServerUpgradeCodec. (.build (Http2FrameCodecBuilder/forServer)) ^"[Lio.netty.channel.ChannelHandler;" (into-array ChannelHandler [nil]))
(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"]
it'd be great if something yelled at me that my type hint is incorrect in that sense
but with an array of objects, i.e., into-array
, you have to tell the compiler what's in the array
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?
@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.
also if deps.edn frequently changes, you will have slightly slower startup time due to classpath calculation
@vincenz.chianese you can look those up via your favorite search engine. Heap memory etc.
Keeping deps edn invocations up to date on the server and in local dev needs attention sometimes
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.
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
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
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.
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.
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
Usually that wouldn't be a problem, but why tempt fate?