Fork me on GitHub

Reading on the web for clojure polymorphism, there is a statement which says use multimetods when you really need them otherwise you just protocols


Do you agree on this in a general manner? Or what is your experience with this, just for curiosity sake 🍶


multimethods are the devil, they can take your nice functions and smear it across several namespaces and change them on import, but sometimes thats what you want (they are also pretty hard to manipulate in the repl)


in general you want to use the most specific/least general abstraction at hand imo, multimethods are extremely open and protocols are contained


protocols are just as open as multimethods


As much I read, multimetods are less performant then protocols. And they dispatch on a multiples values rather then protocols where you have your type dispatch


that's true, they are less performant than protocols if you're only measuring method call dispatch and not the work done by the body of the multimethod

clj 4
Alex Miller (Clojure team)12:07:49

Don’t fear multimethod perf - the overhead might be 70 ns vs 15 ns for protocol calls. Yes, that’s more, but probably dwarfed by the parts of your program doing actual work, unless it’s in a very hot loop.

Alex Miller (Clojure team)12:07:37

There used to be some much bigger perf issues with multimethods. Those were fixed long ago but the fud lingers.

clj 8
Alex Miller (Clojure team)12:07:24

Both are useful. If you are fundamentally doing type based dispatch, then use protocols. If not, use multimethods.

clj 4
Alex Miller (Clojure team)12:07:07

This is an old repo with some timings (needs an update)

Alex Miller (Clojure team)13:07:17

updated those timings. kind of shocking how much better more recent jdk is on a few of those.

clj 4

could it be that your computer is also significantly faster? 🙂

Alex Miller (Clojure team)13:07:53

well I changed 3 things at the same time so it's hard to tell :) but I suspect it's a combination.

Alex Miller (Clojure team)13:07:53

new computer is possibly actually slower in core speed, certainly not much faster. but the places where the general shape of the answer differs are I suspect java

👍 8

Hey all - perhaps a silly question here... I'm trying to use spec-tools and it seems like the conditional reader macro is being ignored when I import the CLJC file. Reference of the line: Error:

user=> (require '[spec-tools.core])
Syntax error (FileNotFoundException) compiling at (spec_tools/impl.cljc:1:1).
Could not locate cljs/analyzer/api__init.class, cljs/analyzer/api.clj or cljs/analyzer/api.cljc on classpath.


I'm invoking it from Java, using tools.deps (updated add-libs branch as of 3 days ago) - am I missing something obvious here?

Alex Miller (Clojure team)16:07:26

That probably won’t work with add-libs


Even if I'm not dynamically loading it? ie. a new clojure process?

Alex Miller (Clojure team)16:07:18

Sorry, I misread above, thought you said reader literals

Alex Miller (Clojure team)16:07:41

I assume you loaded it as a maven artifact?

Alex Miller (Clojure team)16:07:20

add-libs will try to add a lib and it’s transitive deps in a way that meshes with the current full deps you have


are you sure the version of spec-tools you are using matches the code you are looking at on master?

Alex Miller (Clojure team)16:07:09

Depending what those are, it may have used an older version of a dep that is incompatible with spec-tools


if the dependency is on version 0.1.1 or later

Alex Miller (Clojure team)16:07:51

Can you try adding it to your deps and restarting your repl to remove the add-lib variable?

Alex Miller (Clojure team)16:07:50

And what do you mean by “loading it from Java”?


Hey! Sorry all. Of course I get a call just as I ask...


So, clojure -Stree reports spec-tools as 0.10.0 - so I think we are good there.

Jamie Rumbelow16:07:08

Hi all. I’m getting ready to make the case for Clojure for a new work project (to a team who still use select! in ruby). One thing I’m concerned about is the additional deployment and maintenance overhead, particularly since I’ve not provisioned a server to run the JVM before (I come from a LAMP background). What’s the state of the art in deploying clojure apps right now? Are there standard, well-maintained docker images you can point an nginx instance at? And has anybody here worked with applications spread across JVM and non-JVM tech?


Loading it from java, I just mean that it's a Java process (so I expect cljs reader conditional to be ignored)

Chris Swanson16:07:56

@jamie962 I don't think you need to worry too much. you can deploy clj apps to heroku if you expect scale, or if you are just running internally, all you need is a JRE installed


Got it working! Ended up deleting .cpcache... Not sure why that fixed it, but 🤷

Chris Swanson16:07:38

personally for deploying clj apps internally, i'd use a docker image with java installed and just build and copy the jar file in

Jamie Rumbelow16:07:47

@chrisjswanson thanks - good to know. we deploy to a kubernetes cluster anyway so the docker angle sounds like a good bet.

Chris Swanson16:07:01

well the above may be your best friend then 🙂

Jamie Rumbelow16:07:43

so i can just copy an uberjar in to that container and then run it with java -jar?

Jamie Rumbelow16:07:54

that’s easier than I expected

Chris Swanson16:07:29

since you use k8s you have no worry of watching the process lifecycle, it's already handled

Chris Swanson16:07:37

just java -jar and be free 🙂

Jamie Rumbelow16:07:48

super! thank you!

Jamie Rumbelow16:07:14

if I did need to worry about process lifecycle, what sort of concerns would there be?

Jamie Rumbelow16:07:23

does the jvm quit after it hits memory limits?

Chris Swanson16:07:58

well if you were just running in a vm , for example, then if your app crashed you'd need a nanny script to detect that and re-run it

Jamie Rumbelow16:07:28

but that’s not a jvm/clj specific problem

Chris Swanson16:07:29

but k8s will do it for you, just launch a new instance of the image

Chris Swanson16:07:49

java will crash if you go over your heap size

Chris Swanson16:07:00

so if anticipate high memory usage,

Jamie Rumbelow16:07:04

I’m pretty au fait with most of this stuff, it’s where we sidle into jvm territory that I begin to lose confidence


If you're using an uberjar to deploy, I suggest the adoptopenjdk images

👍 4

If you need lein at runtime that's a different deal...


We use lein to build an uberjar, then run it in a standard Java container with java -jar

Chris Swanson16:07:21

clojure image maybe more intended for dev, and then just deploy with adoptopenjdk image. same pattern applies

Chris Swanson16:07:56

you don't have to worry too much about JVM , I've found the metrics it exposes to actually be quite helpful. do look into key jvm metrics to track though, they can let you know if your app leaks memory, etc


Not coming from jvm world. What is the meaning for snapshot at the end of the release numbers? Can be removed?


Is this applicable for clojure too?


it generally means "code that will eventually become the given version"


so 0.11.7-SNAPSHOT means, this is some code that exists somewhere between the release of 0.11.7 and the version before 0.11.7


OK so one should remember to remove it when realising 😁


...between 0.11.6 and...


so if you were cutting a release of some project you might switch from the snapshot version to the release version, cut the release, and then switch it to the next snapshot version


in general using snapshots is bad, it is using unreleased software


Another important thing to remember is that 0.11.7-SNAPSHOT can be a different artifact at different times so you don't want to depend on an artifact by name that could change out from under you.


a lot of projects don't publish snapshot artifacts anyway


For example, Clojure has daily builds and they are all named 1.11.0-master-SNAPSHOT every day -- and each of them could be different builds.


OK thx I got it clj clojure-berlin


I might remove it


Since I don't plan to do so often releases.'😁


Currently I'm trying to do a o.o.1:grin:


Does this exception make sense to folks?

java.lang.ClassCastException: com.badlogic.gdx.physics.bullet.collision.SWIGTYPE_p_p_btCollisionObject cannot be cast to com.badlogic.gdx.physics.bullet.collision.btCollisionObject
Is the SWIGTYPE version of btCollisionObject something clojure came up with that makes it so it can't compare itself against the java version of the class?


Oh, looks like SWIG is something to do with the C++ bindings

jason poage22:07:06

So it seems im back to my original problem with clojurescript. I have a script I import into my nodejs project using a <script> tag in the public html file. now im trying to do unit testing and this workaround isnt working anymore. Any ideas of how to import a compiled clojurescript into a nodejs app?


you have a node.js server that is showing an HTML file with a ClojureScript app added via a script tag?

jason poage23:07:54

but now i cant even get my cljs file to build and im not sure what happened…

jason poage23:07:03

it doesnt give me a file name

jason poage23:07:58

java.lang.ClassCastException: clojure.lang.PersistentVector cannot be cast to clojure.lang.IPersistentMap at clojure.lang.RT.dissoc ( clojure.core$dissoc.invokeStatic (core.clj:1511) clojure.core$dissoc.invoke (core.clj:1504) leiningen.cljsbuild.config$backwards_compat_crossovers$fn__854.invoke (config.clj:70) clojure.core$map$fn__5851.invoke (core.clj:2753) clojure.lang.LazySeq.sval ( clojure.lang.LazySeq.seq ( clojure.lang.RT.seq ( clojure.lang.LazilyPersistentVector.create ( clojure.core$vec.invokeStatic (core.clj:377) clojure.core$vec.invoke (core.clj:367) leiningen.cljsbuild.config$backwards_compat_crossovers.invokeStatic (config.clj:70) leiningen.cljsbuild.config$backwards_compat_crossovers.invoke (config.clj:63) leiningen.cljsbuild.config$backwards_compat.invokeStatic (config.clj:77) leiningen.cljsbuild.config$backwards_compat.invoke (config.clj:76) leiningen.cljsbuild.config$normalize_options.invokeStatic (config.clj:152) leiningen.cljsbuild.config$normalize_options.invoke (config.clj:148) leiningen.cljsbuild.config$extract_options.invokeStatic (config.clj:185) leiningen.cljsbuild.config$extract_options.invoke (config.clj:179) leiningen.cljsbuild$cljsbuild.invokeStatic (cljsbuild.clj:258) leiningen.cljsbuild$cljsbuild.doInvoke (cljsbuild.clj:249) clojure.lang.RestFn.applyTo ( clojure.lang.Var.applyTo ( clojure.core$apply.invokeStatic (core.clj:667) clojure.core$apply.invoke (core.clj:660) leiningen.core.main$partial_task$fn__6592.doInvoke (main.clj:284) clojure.lang.RestFn.applyTo ( clojure.lang.AFunction$1.doInvoke ( clojure.lang.RestFn.applyTo ( clojure.core$apply.invokeStatic (core.clj:667) clojure.core$apply.invoke (core.clj:660) leiningen.core.main$apply_task.invokeStatic (main.clj:334) leiningen.core.main$apply_task.invoke (main.clj:320) leiningen.core.main$resolve_and_apply.invokeStatic (main.clj:343) leiningen.core.main$resolve_and_apply.invoke (main.clj:336)$do.invokeStatic (do.clj:40)$do.doInvoke (do.clj:32) clojure.lang.RestFn.applyTo ( clojure.lang.Var.applyTo ( clojure.core$apply.invokeStatic (core.clj:667) clojure.core$apply.invoke (core.clj:660) leiningen.core.main$partial_task$fn__6592.doInvoke (main.clj:284) clojure.lang.RestFn.applyTo ( clojure.lang.AFunction$1.doInvoke ( clojure.lang.RestFn.applyTo ( clojure.core$apply.invokeStatic (core.clj:667) clojure.core$apply.invoke (core.clj:660) leiningen.core.main$apply_task.invokeStatic (main.clj:334) leiningen.core.main$apply_task.invoke (main.clj:320) leiningen.core.main$resolve_and_apply.invokeStatic (main.clj:343) leiningen.core.main$resolve_and_apply.invoke (main.clj:336) leiningen.core.main$main$fn_6681.invoke (main.clj:452) leiningen.core.main$_main.invokeStatic (main.clj:442) leiningen.core.main$_main.doInvoke (main.clj:439) clojure.lang.RestFn.applyTo ( clojure.lang.Var.applyTo ( clojure.core$apply.invokeStatic (core.clj:665) clojure.main$main_opt.invokeStatic (main.clj:491) clojure.main$main_opt.invoke (main.clj:487) clojure.main$main.invokeStatic (main.clj:598) clojure.main$main.doInvoke (main.clj:561) clojure.lang.RestFn.applyTo ( clojure.lang.Var.applyTo ( clojure.main.main (


my guess would be an error in your project.clj


that would make sense for the error that was posted


as for unit tests, it doesn't sound like it has anything to do with node.js


once you've resolved the above error: how are you trying to run unit tests?

jason poage23:07:45

yeah that was most definitely the issue. thanks

jason poage23:07:00

the error above is independent from my nodejs problem. but i think i found what im looking for online, finally, but i wont know until i try it and see if it works. it is definitely something i havent tried yet so its worth a shot

jason poage23:07:58

i was trying what was described here and im having no luck so far


@jason821 it sounds like you don't want to run your ClojureScript in Node.js though


you're including it as a script tag on an HTML page?


so is the ClojureScript supposed to run in the browser as a UI app, or is it supposed to run on the server side in Node.js?

jason poage23:07:14

which ever way it works tbh, but preferably in nodejs

jason poage23:07:27

because most of my project is written in javascript


that doesn't make sense to me


Node.js runs on the server, so you're not going be able to, for example, update the page dynamically based on your clojurescript code


what does your clojurescript code do?

jason poage23:07:25

i dont want to dynamically load it as clojurescript, nodejs will automatically refresh if anything changes

jason poage23:07:47

so if i have a compiled js file that works on nodejs, thats what i need


there are ways to compile CLJS to be imported into a Node.js runtime


I don't really know what you mean by "dynamically load it as clojurescript"


shadow-cljs is a build tool which has built-in configurations for developing node modules

jason poage23:07:05

that was your verbage lol

jason poage23:07:37

i compile the cljs manually, the nodejs will update it. ideally i would like to have cljs reload but thats not how my project is setup

jason poage23:07:00

im trying out shadow cljs and im trying to figure out if it will compile it to javascript to load in my nodejs app by importing it from javascript files

jason poage23:07:46

i could just be going about it the wrong way either way, the js file needs to import the clojurescript after its compiled to javascript

jason poage23:07:33

i have an idea, maybe im suppose to load the javascript app with shadow cljs instead of the other way around. but im not too sure since the app starting out being javascript