clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
2020-10-24T01:06:33.138100Z

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)?

2020-10-24T01:27:43.138900Z

If it's a defrecord use keyword lookup

seancorfield 2020-10-24T01:39:42.139400Z

@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=>

seancorfield 2020-10-24T01:40:47.140700Z

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.

2020-10-24T02:42:18.141400Z

Changing the .field to :field worked. Thanks.

valerauko 2020-10-24T12:40:47.142300Z

I'm running into a problem with type hinting a constructor. Even with the type hints it complains ctor can't be resolved.

valerauko 2020-10-24T12:41:58.142400Z

the constructor is like public A(B firstParam, C... secondParam)

valerauko 2020-10-24T12:43:03.142600Z

my call is like (A. ^B b-instance ^C c-instance) but this doesn't work

valerauko 2020-10-24T12:43:31.142800Z

i've tried ^[Lfully.qualified.C [c-instance] too but that didn't work either

schmee 2020-10-24T12:45:04.143Z

you might need something like (A. b-instance (into-array C c-instance))

valerauko 2020-10-24T12:49:58.143300Z

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)

valerauko 2020-10-24T12:52:02.143500Z

it keeps telling me that call to io.netty.handler.codec.http2.Http2ServerUpgradeCodec ctor can't be resolved. and fails to actually work

valerauko 2020-10-24T12:54:03.143700Z

sadly it doesn't go into detail why it can't resolve the constructor (listing the argument types)

valerauko 2020-10-24T12:54:29.143900Z

the ctor in question is public too

schmee 2020-10-24T12:55:26.144100Z

you need to pass (into-array ChannelHandler []) as a second argument

schmee 2020-10-24T12:55:38.144300Z

Java interop doesn’t let you ignore varargs

valerauko 2020-10-24T12:56:22.144500Z

i've tried both with (into-array ChannelHandler [handler]) and (into-array ChannelHandler handler) and neither worked

schmee 2020-10-24T12:57:54.144700Z

so this doesn’t work?

(Http2ServerUpgradeCodec. (.build (Http2FrameCodecBuilder/forServer)) (into-array ChannelHandler [handler]))

valerauko 2020-10-24T12:59:51.145Z

yes it still tells me ctor can't be resolved

schmee 2020-10-24T13:05:20.145200Z

then I don’t know

valerauko 2020-10-24T13:09:08.145400Z

i tried making a static java method that does the same and it resolves the warning

valerauko 2020-10-24T13:09:11.145600Z

weird stuff

dharrigan 2020-10-24T13:37:53.146100Z

Try this

dharrigan 2020-10-24T13:38:00.146300Z

(Http2ServerUpgradeCodec. (.build (Http2FrameCodecBuilder/forServer)) ^"[Lio.netty.channel.ChannelHandler;" (into-array ChannelHandler [nil]))

dharrigan 2020-10-24T13:38:15.146500Z

(naturally, replace the nil with your handler :-))

dharrigan 2020-10-24T13:38:52.146700Z

(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"]

dharrigan 2020-10-24T13:39:09.146900Z

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"]

valerauko 2020-10-24T13:54:47.147100Z

arrgh was it the ampersand?

dharrigan 2020-10-24T13:54:53.147300Z

no

dharrigan 2020-10-24T13:54:59.147500Z

[Lio.netty.channel.ChannelHandler;"

valerauko 2020-10-24T13:55:09.147700Z

i tried that, but without the ampersand

dharrigan 2020-10-24T13:55:19.147900Z

you mean semicolon?

dharrigan 2020-10-24T13:55:32.148100Z

ampersand = &

valerauko 2020-10-24T13:55:34.148300Z

sorry, that. at this point i'm confusing words

dharrigan 2020-10-24T13:55:48.148700Z

yeah, missing the semicolon 🙂

valerauko 2020-10-24T13:55:58.149Z

that's just SOOO java lol

valerauko 2020-10-24T13:56:02.149300Z

you saved the day, thanks!"

vncz 2020-10-24T13:56:10.149600Z

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?

dharrigan 2020-10-24T13:56:39.149700Z

you're most welcome, the ; is part of the class format

dharrigan 2020-10-24T13:56:47.149900Z

(or rather field descriptors)

valerauko 2020-10-24T13:57:19.150100Z

it'd be great if something yelled at me that my type hint is incorrect in that sense

borkdude 2020-10-24T13:58:03.151100Z

@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.

dharrigan 2020-10-24T13:58:23.151200Z

That would be handy, but no 🙂

dharrigan 2020-10-24T13:58:38.151600Z

4.3.2. Field Descriptors

borkdude 2020-10-24T13:59:03.152300Z

also if deps.edn frequently changes, you will have slightly slower startup time due to classpath calculation

vncz 2020-10-24T13:59:23.152800Z

@borkdude Thanks for the answer; what JVM settings are you referring to?

dharrigan 2020-10-24T13:59:24.152900Z

For primitives, it's fine, i.e., ^long, ^double and so on

dharrigan 2020-10-24T13:59:38.153100Z

but with an array of objects, i.e., into-array, you have to tell the compiler what's in the array

dharrigan 2020-10-24T14:00:10.153600Z

and for that, you need to use that construct, i.e., "[Ljava.lang.String;"

dharrigan 2020-10-24T14:00:22.154100Z

I learnt the hard way too 🙂

borkdude 2020-10-24T14:00:38.154500Z

@vincenz.chianese you can look those up via your favorite search engine. Heap memory etc.

vncz 2020-10-24T14:00:51.154800Z

Ah ok ok I thought there was something specific for Clojure's startup

borkdude 2020-10-24T14:01:30.155400Z

I think in most cases both approaches should work well

borkdude 2020-10-24T14:02:14.156Z

Keeping deps edn invocations up to date on the server and in local dev needs attention sometimes

borkdude 2020-10-24T14:02:36.156400Z

(e.g. -A won't invoke main in a couple of months from now)

2020-10-24T16:58:40.159300Z

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.

2020-10-24T16:59:10.159600Z

Usually that wouldn't be a problem, but why tempt fate?

p-himik 2020-10-24T17:28:16.159700Z

How? Assuming, there are no snapshots and no overwritten artifacts and that you deploy the same deps.edn everywhere.

2020-10-24T17:39:48.159900Z

Sometimes people do terrible things like publish a new artifact with the same version

2020-10-24T17:41:10.160100Z

(google actually did this with guava)

2020-10-24T17:42:04.160300Z

Or tools-deps could have a big that causes into resolve the deps differently on one of your servers

2020-10-24T17:45:30.160500Z

Basically dependency resolution and fetching is a complicated process, your deploys will be more reliable of it is not part of the deploy

p-himik 2020-10-24T17:45:47.160700Z

Ah, that's what I meant by "overwritten artifacts", yeah. Having a bug is also reasonable, thanks.

2020-10-24T17:56:19.160900Z

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

2020-10-24T17:59:58.161100Z

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

p-himik 2020-10-24T18:03:37.161300Z

Have you seen anything promising or even production ready that handles the latter scenario?

2020-10-24T18:06:25.161500Z

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

p-himik 2020-10-24T18:07:16.161700Z

Thanks!

2020-10-24T18:14:32.161900Z

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

p-himik 2020-10-24T18:18:23.162100Z

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

2020-10-24T18:30:59.162400Z

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?

2020-10-24T18:31:35.162600Z

If so, as I believe is true, that can happen at times unknown to you, the user of the dependency.

2020-10-24T18:32:31.162800Z

it doesn't

2020-10-24T18:33:07.163Z

the take the most recent thing is only if you depend on a given library twice with different versions

2020-10-24T18:33:23.163200Z

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.

2020-10-24T18:34:14.163400Z

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?

2020-10-24T18:34:39.163600Z

I'm sorry, the maximum, but no later, is what I meant to ask.

2020-10-24T18:34:48.163800Z

yes

2020-10-24T18:35:13.164Z

OK, yeah, that is definitely more reproducible than taking a later version than the maximum.

p-himik 2020-10-24T18:37:22.164200Z

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