Fork me on GitHub
#clojure
<
2019-09-06
>
seancorfield01:09:50

@ag here's an example

(! 694)-> lein new compojure ag/slurp-example

Thu Sep 05 18:28:51
(sean)-(jobs:0)-(~/clojure)
(! 695)-> cd slurp-example/

Thu Sep 05 18:28:54
(sean)-(jobs:0)-(~/clojure/slurp-example)
(! 696)-> cat project.clj 
(defproject ag/slurp-example "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :min-lein-version "2.0.0"
  :dependencies [[org.clojure/clojure "1.10.0"]
                 [compojure "1.6.1"]
                 [ring/ring-defaults "0.3.2"]]
  :plugins [[lein-ring "0.12.5"]]
  :ring {:handler ag.slurp-example.handler/app}
  :profiles
  {:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
                        [ring/ring-mock "0.3.2"]]}})

Thu Sep 05 18:28:56
(sean)-(jobs:0)-(~/clojure/slurp-example)
(! 697)-> lein repl
...
user=> (take 100 (slurp ( "compojure/core.clj")))
(\( \n \s \space \c \o \m \p \o \j \u \r \e \. \c \o \r \e \newline \space \space \" \A \space \D \S \L \space \f \o \r \space \b \u \i \l \d \i \n \g \space \R \i \n \g \space \h \a \n \d \l \e \r \s \space \f \r \o \m \space \s \m \a \l \l \e \r \space \r \o \u \t \e \s \. \newline \newline \space \space \C \o \m \p \o \j \u \r \e \space \r \o \u \t \e \s \space \a \r \e \space)
user=> 

ag16:09:57

Oh wow… That’s an extremely elaborate answer. Thank you Sean.

seancorfield18:09:42

It's really just one line of code -- the rest was all setup 🙂 But I find sharing a REPL session to be a good way to illustrate solutions.

seancorfield01:09:49

All the files in all the libraries that your project depends on are available directly from the classpath at runtime. You just have to know the relative path of the file you want.

ahungry04:09:22

hey all - I asked before awhile ago, but don't think I saw an answer. How would I use spec fdef to define this function of 0 arity, as well as instrument the return value for correctness? (defn get-num [] 42)

seancorfield04:09:09

@m131 instrument does not check :ret or :fn -- only :args: it checks that calls made to the function are correct. check is what you use to ensure that the behavior of the function is correct. Here's an example based on your get-num:

user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as st])
nil
user=> (defn get-num [] 42)
#'user/get-num
user=> (s/fdef get-num :args (s/cat) ; empty argument list
                       :ret int? ; returns an int
                       :fn #(= 42 (:ret %)) ; describes properties of the
                                            ; function in terms of :args
                                            ; and :ret
)
user/get-num
user=> (st/instrument `get-num)
[user/get-num]
user=> (get-num :foo)
Execution error - invalid arguments to user/get-num at (REPL:1).
(:foo) - failed: Extra input
user=> (get-num)
42
user=> (st/check `get-num)
({:spec #object[clojure.spec.alpha$fspec_impl$reify__2524 0xaa549e5 "clojure.spec.alpha$fspec_impl$reify__2524@aa549e5"], :clojure.spec.test.check/ret {:result true, :pass? true, :num-tests 1000, :time-elapsed-ms 59, :seed 1567744480324}, :sym user/get-num})
user=> 

seancorfield04:09:25

Then if we change get-num to return a wrong value:

user=> (defn get-num [] 13)
#'user/get-num
user=> (st/instrument `get-num) ; we need to instrument the new definition to get call checking!
[user/get-num]
user=> (get-num :foo)
Execution error - invalid arguments to user/get-num at (REPL:1).
(:foo) - failed: Extra input
user=> (st/check `get-num)
({:spec #object[clojure.spec.alpha$fspec_impl$reify__2524 0xaa549e5 "clojure.spec.alpha$fspec_impl$reify__2524@aa549e5"], :clojure.spec.test.check/ret {:shrunk {:total-nodes-visited 0, :depth 0, :pass? false, :result #error {
 :cause "Specification-based check failed"
 :data {:clojure.spec.alpha/problems [{:path [:fn], :pred (clojure.core/fn [%] (clojure.core/= 42 (:ret %))), :val {:args {}, :ret 13}, :via [], :in []}], :clojure.spec.alpha/spec #object[clojure.spec.alpha$spec_impl$reify__2059 0x6eeade6c "clojure.spec.alpha$spec_impl$reify__2059@6eeade6c"], :clojure.spec.alpha/value {:args {}, :ret 13}, :clojure.spec.test.alpha/args (), :clojure.spec.test.alpha/val {:args {}, :ret 13}, :clojure.spec.alpha/failure :check-failed}
(long stack trace omitted)

ahungry04:09:32

thanks - is the :ret keyword actually used in anyway? or just documentation? I assume check is just doing something like the generative testing?

ahungry04:09:45

(and comparing vs what is in the fn kw form?)

seancorfield04:09:13

check verifies that the result of calling get-num conforms to whatever :ret specifies -- if :ret is present -- and it also checks whatever :fn specifies (again, if present), passing in a hash map with {:args {}, :ret 13} in this case.

seancorfield04:09:42

Both :ret and :fn are optional. Sometimes you'll want both, sometimes you'll just want one of them.

seancorfield04:09:59

The :args hash map has keys named for each argument.

ahungry04:09:25

So, what am I missing here? No output is given, just a nil eval result to both stest calls):

(s/fdef get-num :args (s/cat) :ret int? :fn  #(int? (:ret %)))
         (defn get-num [] "dog")
         (stest/instrument `get-num)
         (stest/check `get-num)

seancorfield04:09:29

And, yes, check is all about generative testing. It uses the :args spec to generative conforming argument data, calls the function, then checks the result (using :ret and/or :fn).

seancorfield04:09:56

(! 703)-> clj -A:test
Clojure 1.10.1
user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as stest])
nil
user=> (s/fdef get-num :args (s/cat) :ret int? :fn  #(int? (:ret %)))
user/get-num
user=> (defn get-num [] "dog")
#'user/get-num
user=> (stest/instrument `get-num)
[user/get-num]
user=> (stest/check `get-num)
({:spec #object[clojure.spec.alpha$fspec_impl$reify__2524 0xa4ca3f6 "clojure.spec.alpha$fspec_impl$reify__2524@a4ca3f6"], :clojure.spec.test.check/ret {:shrunk {:total-nodes-visited 0, :depth 0, :pass? false, :result #error {
 :cause "Specification-based check failed"
 :data {:clojure.spec.alpha/problems [{:path [:ret], :pred clojure.core/int?, :val "dog", :via [], :in []}], :clojure.spec.alpha/spec #object[clojure.spec.alpha$spec_impl$reify__2059 0x698122b2 "clojure.spec.alpha$spec_impl$reify__2059@698122b2"], :clojure.spec.alpha/value "dog", :clojure.spec.test.alpha/args (), :clojure.spec.test.alpha/val "dog", :clojure.spec.alpha/failure :check-failed}
I suspect you had a definition of get-num in your REPL already and the s/fdef referred to that, but then you redefined get-num?

ahungry04:09:25

probably - for repl based workflow, what would I have to do to avoid that pitfall? Reset the repl session ?

ahungry04:09:35

I did try to re-eval the 4 lines in order

seancorfield04:09:39

(BTW, you need test.check as a dependency for stest/check to work -- that's what my -A:test alias adds)

seancorfield04:09:01

You could eval the s/fdef after the defn of get-num, just to be sure.

ahungry04:09:49

oh yup, this is missing test.check in this particular toy-project. Is there a reason it isn't just included? Without it, can stest/check ever do anything useful/

seancorfield04:09:49

(although, FWIW, when I ran those in the REPL with an existing get-num definition, it still worked for me)

seancorfield04:09:57

clojure.spec.alpha doesn't need it for anything. Only clojure.spec.test.alpha needs it -- so it is lazy-loaded in there.

seancorfield04:09:20

It's expected that folks would use clojure.spec.alpha in production code, but clojure.spec.test.alpha only in dev/test code.

seancorfield04:09:38

(and you don't want test.check included in your production code in general)

seancorfield04:09:58

Without test.check, I get this:

(! 706)-> clj
Clojure 1.10.1
user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as stest])
nil
user=> (s/fdef get-num :args (s/cat) :ret int? :fn  #(int? (:ret %)))
user/get-num
user=> (defn get-num [] "dog")
#'user/get-num
user=> (stest/instrument `get-num)
[user/get-num]
user=> (stest/check `get-num)
({:spec #object[clojure.spec.alpha$fspec_impl$reify__2524 0x774698ab "clojure.spec.alpha$fspec_impl$reify__2524@774698ab"], :clojure.spec.test.check/ret {:result #error {
 :cause "Could not locate clojure/test/check/generators__init.class, clojure/test/check/generators.clj or clojure/test/check/generators.cljc on classpath."

ahungry04:09:26

same - thanks - I was able to reproduce your results with clj directly and not attempting in the lein project that had other dependencies

seancorfield04:09:11

Cool. I always try to repro stuff with the smallest set of deps possible, and always with clj, in order to avoid any weirdness that comes from Leiningen plugins or other project artifacts.

pyr06:09:31

Hi Clojurians, if any of you are stuck in SQL land for some applications but still want pull-like syntax on your entities, here's a library for that: https://exoscale.github.io/seql/

pyr07:09:01

seql is much more narrow than walkable in size, scope, and intent: - it can target a single database instance at a time - it can only target SQL databases - it is a library, not a full fledged server (though we have the server bits internally which will be open soon)

pyr07:09:30

the library part allows us to use that in several places throughout the codebase in places where we don't necessarily want to have RPC overhead

schmee07:09:48

I like the sound of that, thanks!

seancorfield07:09:21

@U06V097TP It looks very nice. Have you considered using next.jdbc instead of clojure.java.jdbc? (happy to follow up in #sql rather than this thread)

😄 4
pyr08:09:51

@U04V70XH6 yes, didn't know about #sql, will join

myguidingstar17:09:44

hi @U06V097TP, Walkable's author here. It's great to see EQL being adopted among Clojure community. What about introducing it in #announcements?

myguidingstar17:09:47

about the last two points you made about the differences between walkable and seql, walkable targets SQL db only, too and it's also just a library - you have to put it behind Ring/Pedestal if you want a real server - or you can use it in a react native (cljs) project to read from a local sqlite file so no server involved there

myguidingstar17:09:19

walkable doesn't support mutations at all, however

smw08:09:49

How do I get this to be realized?

vlaaad09:09:29

@smw You already did with doall

smw09:09:25

@vlaaad Ok. How can I transform it into a form that I can print?

Daniel Stephens09:09:34

You've called string on a LazySeq so you now have a string of value equal to clojure.lang.LazySeq..., maybe you want to do (apply str (interpose ", " ["one" "two" "three"]))

smw09:09:23

That's exactly what I want. Thank you!

Daniel Stephens09:09:29

to the same affect but more readable you could also use clojure.string/join

8
vlaaad09:09:02

there is also clojure.string/join for that

smw09:09:03

Yeah, actually I was trying to do clojurescript koans, and it wouldn't accept an answer with c.s/join

smw09:09:13

Thank you very much for the help.

👍 4
geraldodev12:09:42

what is the name of clojure tool to visualize edn ?

geraldodev12:09:09

that one from cognitect to visualize clojure data

Jakub Holý (HolyJak)12:09:47

Ah, ok. I would call that rather a "data browser" 🙂

Jakub Holý (HolyJak)12:09:31

Hello, can defaults in destructuring (`:or`) be used with namespaced keywords? https://clojure.org/guides/destructuring#_namespaced_keywords does not mention it and none of :person/or {age 0} / :or {person/age 0} / :or {:person/age 0} seems to work. So I guess I have to use old good (or age 0) inside the function body. Right?

Alex Miller (Clojure team)12:09:34

The keys of :or are the local symbol names

Alex Miller (Clojure team)12:09:55

So I would expect :or {age 0} to work

❤️ 4
4
Karol Wójcik12:09:15

Which protocol/interface defines dissoc? I cannot see any 😞

Alex Miller (Clojure team)12:09:18

I think it’s IPersistentMap

Alex Miller (Clojure team)12:09:26

Yeah, it’s called without there

tengstrand12:09:05

I'm writing a blog post about the origin of complexity where I want to use one slide from Rich Hickey's talk Simple Made Easy. I don't know how to get into contact with Rich, but maybe @alexmiller can ask him? I will link to the talk also and give him all the credits!

lodin13:09:49

Which blog is it?

tengstrand14:09:00

Thanks! It's on Medium, not a personal blog. One example is this one: https://medium.com/@joakimtengstrand/the-polylith-architecture-1eec55c5ebce

tengstrand16:09:49

I have posted it now @alexmiller. You may also send a special thank to Rich Hickey from me if you see him, he has been a great inspiration!: https://medium.com/@joakimtengstrand/the-origin-of-complexity-8ecb39130fc

g14:09:32

is there any reasonably simple way to get the runtime of all tests in a project?

g14:09:47

first thought was a hook into deftest that wraps everything in time

Alex Miller (Clojure team)14:09:37

there are reporting hooks in clojure.test, not sure if they are sufficient to do this off the top of my head

g14:09:29

ah, you’re right! thanks

borkdude15:09:58

@gtzogana I've solved this problem in https://github.com/borkdude/advent-of-cljc where the run time of each solution (= test) is recorded into a database.

🙏 4
✔️ 4
borkdude15:09:04

@gtzogana I wrote my own time macro which returns the time took and the return value: https://github.com/borkdude/advent-of-cljc/blob/master/src/aoc/utils.cljc#L99

borkdude15:09:37

and wrote my own deftest macro which uses that

borkdude15:09:01

maybe a better way is to use metadata with the time took on the result value, but that won't work for non IObjs

g15:09:45

thanks! i found this https://gist.github.com/jcf/a1e3781b8c091a00c5d9 which seems to be the most lightweight solution at present

borkdude15:09:13

nice, so it can be done using those hooks 🙂

kirill.salykin15:09:13

seems like it should be array of java functions because queries are varargs

borkdude15:09:42

@kirill.salykin does (into-array ...) work?

kirill.salykin15:09:59

dunno how to make ref to a function…

borkdude15:09:39

why a function?

kirill.salykin15:09:40

(into-array java.time.LocalDateTime/from) doesnt work

kirill.salykin15:09:50

this is example: `

kirill.salykin15:09:56

TemporalAccessor dt = parser.parseBest(str, ZonedDateTime::from, LocalDateTime::from);
  if (dt instanceof ZonedDateTime) {
   ...
  } else {
   ...
  }

kirill.salykin15:09:38

Maybe I misunderstand it - but it seems like function ZonedDateTime::from

borkdude15:09:33

what happens if you pass a function with (into-array [#(prn %)]) for example?

borkdude15:09:11

you can pass varargs as array in Clojure in java interop

kirill.salykin15:09:06

lemme try feels a bit weird to pass clojure wrapper for java function

kirill.salykin15:09:43

nope

Cannot cast [L...$eval91504$fn__91505; to
   [Ljava.time.temporal.TemporalQuery;

borkdude15:09:18

how do you get a parser?

kirill.salykin15:09:47

Just a formatter

borkdude15:09:24

maybe you can get a method reference using reflection? I give up 🙂

borkdude15:09:29

have to go afk now

borkdude15:09:28

I would probably write my own parse-best in this case

kirill.salykin15:09:32

Thanks, will try

schmee15:09:46

@kirill.salykin you need to wrap your function in a reify, something like:

(defn fn->temporal-query [f]
  (reify TemporalQuery
    (queryFrom [_ x] (f x))))

schmee15:09:11

and since the method is varargs, you also need to pass it as (into-array TemporalQuery [...your wrapped fns...])

schmee15:09:34

this is like this worst case possible with Clojure’s Java interop 🙂

Alex Miller (Clojure team)16:09:20

user=>
(import '[java.time.format DateTimeFormatter]
        '[java.time ZonedDateTime LocalDateTime]
         '[java.time.temporal TemporalQuery])
java.time.temporal.TemporalQuery
user=>
user=> (def fmt DateTimeFormatter/ISO_ZONED_DATE_TIME)
#'user/fmt
user=>
(def tqs
  [(reify TemporalQuery (queryFrom [_ t] (ZonedDateTime/from t)))
   (reify TemporalQuery (queryFrom [_ t] (LocalDateTime/from t)))])
#'user/tqs
user=>
user=> (.parseBest fmt "2019-01-02T12:34:56+00:00" (into-array TemporalQuery tqs))
#object[java.time.ZonedDateTime 0x23ee75c5 "2019-01-02T12:34:56Z"]

Alex Miller (Clojure team)16:09:36

you can also do this using the MethodHandles interface, not sure if that's cleaner or not

Alex Miller (Clojure team)16:09:45

prob no picnic either way

Alex Miller (Clojure team)16:09:02

that would be through java.lang.invoke.MethodHandles.lookup().findStatic() here I think

Alex Miller (Clojure team)16:09:29

I'd say it's probably not cleaner :) but does avoid the wrapper

Alex Miller (Clojure team)16:09:33

getting a static method handle is like (.findStatic (MethodHandles/lookup) java.time.ZonedDateTime "from" (MethodType/methodType java.time.ZonedDateTime (into-array Class [TemporalAccessor])))

Alex Miller (Clojure team)16:09:59

but even that needs some kind of cast or coercion to be a TemporalQuery

ag17:09:14

What’s the best way to parse javascript? Here’s the catch: how to do it in clojure, not clojurescript

markus17:09:41

I misread it as "What is the best way to praise javascript?" 😄 Sorry, don't have a good answer to the actual question. I don't work with web stuff much. But I guess there are Java libraries that can do it?

borkdude18:09:32

What's the use case?

ag18:09:14

it's complicated :)

ag18:09:49

I'm going to give google-caja a try, see if that works

borkdude18:09:19

I wonder if @U05224H0W's shadow-cljs does any JS parsing?

borkdude18:09:28

to support npm stuff maybe

ag18:09:36

ha… yeah, I forgot about the google-closure. Thanks Michiel!

borkdude18:09:20

not sure if it's intended for "other tools" usage, but if you have any luck with it, lemme know, I want to do some JS parsing as well at some point

ag18:09:05

sure, I will share my findings (if there are any). It’s just an experiment, I may get bored soon and get back to my normal, boring life. haha.

borkdude18:09:20

might be nice to have a Clojure interface to that thing

borkdude18:09:26

looks pretty useful:

user=> (def prog (.parseProgram (Parser. (Parser$Config.) nil (SourceFile. "foo" "var x = 10;"))))
#'user/prog
user=> (.-sourceElements prog)
[#object[com.google.javascript.jscomp.parsing.parser.trees.VariableStatementTree 0x18245eb0 "VARIABLE_STATEMENT@<foo(1, 1) - foo(1, 12)>"]]

ag18:09:22

Oh wow, that was quick. What’s Parser$Config here?

borkdude18:09:51

full repro:

$ clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.520"}}}'
Clojure 1.10.1
user=> (import '[com.google.javascript.jscomp.parsing.parser Parser Parser$Config SourceFile])
com.google.javascript.jscomp.parsing.parser.SourceFile
user=> (def prog (.parseProgram (Parser. (Parser$Config.) nil (SourceFile. "foo" "var x = 10;"))))
#'user/prog
user=> (.-sourceElements prog)
[#object[com.google.javascript.jscomp.parsing.parser.trees.VariableStatementTree 0x61bcd567 "VARIABLE_STATEMENT@<foo(1, 1) - foo(1, 12)>"]]

borkdude18:09:36

user=> (.-declarationType (.-declarations (first (.-sourceElements prog))))
#object[com.google.javascript.jscomp.parsing.parser.TokenType 0xf27ea3 "var"]

borkdude18:09:04

it seems they use loads of classes with public properties, which could be useful to coerce into Clojure maps

ag18:09:26

yeah, I’m digging through the methods… gosh I hate Java. I mean everyone does

borkdude18:09:07

I wonder why bean doesn't return public properties

borkdude18:09:48

but maybe using a little bit of reflection coercing it to clojure maps will work

ag18:09:02

This is what I need: if I have a js file with:

var Foo = { First: function() {}, Second: function() {} }
etc. I need to get Object.keys of Foo, i.e.: want to get a vector in clj: ["First", "Second"]

borkdude18:09:55

that seems doable. my use case would be this: https://github.com/borkdude/clj-kondo/issues/90

ag18:09:19

well that… is way above my head… 🙂 but you sir is very smart person. I’m sure you’d figure this out

borkdude18:09:04

well, it's already been done for Java, I just need to get similar information from somewhere about JS... Thanks for bringing this up, now I might have a fun way of doing this 😜

ag18:09:54

Yeah, I guess sometimes it is worth banging your head around a thing nobody ever done before

ag19:09:06

I’m still not sure how to get that list, based on the snippet you have posted:

(def prog (.parseProgram (Parser. (Parser$Config.) nil (SourceFile. "foo" "var Foo = { First: function() {}, Second: function() {} }"))))
What do I do next?

ag19:09:08

.-sourceElements returns list of VariableDeclarationListTree, going down the rabbit hole and doing (.-declarations (first (.-sourceElements prog))) got me nowhere 😞

borkdude19:09:14

this seems to return the left hand side of the assignment:

(.-lvalue (first  (.-declarations (.-declarations (first (.-sourceElements prog))))))

borkdude19:09:29

it takes a bit of looking at the Java code

borkdude19:09:47

user=> (.-identifierToken (.-lvalue (first  (.-declarations (.-declarations (first (.-sourceElements prog)))))))
#object[com.google.javascript.jscomp.parsing.parser.IdentifierToken 0x2d10e0b1 "x"]
look, "x" 🙂

ag19:09:38

OMG… I’m just going deeper and deeper into the weeds of all these classes and it is fucking mind blowing. Now I understand how Clojure community being smaller than number of people working at Google generates so many awesome ideas all the time. Because they don’t have to deal with this terrifying wilderness of OO class hierarchies

ag19:09:54

I’ve come to real sad and dismal realization that so many very talented people actually have to design, implement and support shit like that. This is fucking waste of human potential. Almost useless carbon footprint on this planet.

ag19:09:32

I think I have finally got what I needed.

(def listing "var Foo = { First: function() {}, Second: function() {} }")

  (def prog (.parseProgram (Parser. (Parser$Config.) nil (SourceFile. "" listing))))

  (map
   #(-> % .-name .toString)
   (.-propertyNameAndValues (.-initializer (first (.-declarations (.-declarations (first (.-sourceElements prog))))))))

=> ("First" "Second")
Holy mother of Alan Turing

ag19:09:55

Michiel, you have my deepest and sincerest gratitude. It would’ve taken me much, much longer to figure this out. Thank you!

borkdude19:09:17

wow, nice 🙂

ag19:09:59

I am not done yet, but this is enough for me to get going.

borkdude19:09:00

This might also be useful: datafy

(clojure.pprint/pprint (clojure.datafy/datafy (.getClass (.-declarations (.-declarations (first (.-sourceElements prog)))))))

borkdude20:09:30

user=> (.-literalToken (.-initializer (first (.-declarations (.-declarations (first (.-sourceElements prog)))))))
#object[com.google.javascript.jscomp.parsing.parser.LiteralToken 0x88a8218 "10"]
yeah, pretty cool

borkdude21:09:23

using the inspect function on classes that aren't implemented yet, is a pretty nice way for exploring

borkdude21:09:47

and then implement every little thing step by step gets a pretty nice parse tree in Clojure

ag00:09:48

This is so cool!

ag00:09:35

thank you for sharing the gist

borkdude18:09:20

does anyone happen to know how to set the default font when creating an excel sheel with docjure / poi?

borkdude18:09:43

when I open an "excel" sheet in MacOS Numbers, I get "missing fonts"

seancorfield19:09:17

@kirill.salykin This might well be one of those times where an existing Clojure wrapper library would be worth using. Either https://github.com/dm3/clojure.java-time (which we use at work) or the newer https://github.com/henryw374/cljc.java-time (which also supports ClojureScript). /cc @schmee

👍 4
bostonaholic19:09:43

anyone know of a version of https://github.com/github/scientist for clojure?

st3fan22:09:21

Is clj-http everyone’s go-to http client?

Alex Miller (Clojure team)22:09:23

it's probably the most common

💯 4
borkdude22:09:56

it's my go-to just like cheshire is my go-to JSON parser (same author), although for async stuff I sometimes use aleph

Quest22:09:18

From testing against a medium volume runtime (10+ mil requests/day), we found that http-kit was 4+ ms faster than clj-http inconsistently on requests. We tried tuning clj-http to match http-kit performance but we could never achieve the same latency

Quest22:09:49

That said, clj-http is more standard & easily supports hystrix. More likely to work with New Relic instrumentation too. I'd recommend using it unless you absolutely can't afford the latency hit

awb9922:09:36

I have a weird issue: My clojure app works on my dev machine in both repl and compiled. On my server it throws an error when compiled, but repl works fine. This is the error: error macroexpanding clojure.core/fn at (clojure/core/unify.clj:83:18) Any ideas?

dpsutton22:09:16

how are you running it?

awb9922:09:29

lein repl

awb9922:09:52

that works

awb9922:09:53

lein ring server-headless 5005

Alex Miller (Clojure team)22:09:55

that's an old spec bug that's fixed

awb9922:09:00

this throws up errors.

Alex Miller (Clojure team)22:09:16

you must be seeing old version of the lib in one place and new version in the other

awb9922:09:35

@alexmiller That might be - the error started today when I added spec to some routines.

dpsutton22:09:58

somewhere you are specifying a lein-ring plugin. Make sure you're using 0.12.5 i belive

awb9922:09:24

I am not using core.unify AT ALL.

awb9922:09:38

in my code at least not.

andy.fingerhut22:09:44

It sounds likely that the core.unify library being depended upon is different between non-working and working cases.

andy.fingerhut22:09:14

Or one is using Clojure 1.9.0 or later, which introduced stricter syntax checking of Clojure source code, vs. Clojure 1.8.0 or earlier.

Alex Miller (Clojure team)22:09:45

it's prob pulled in as a transitive dep of the lein-ring plugin as dpsutton says

awb9922:09:51

how can I check clojure version?

borkdude22:09:54

lein deps :tree

dpsutton22:09:37

@hoertlehner is this a public repo?

awb9922:09:48

private repo

borkdude22:09:30

@hoertlehner Maybe you could post the output of lein deps :tree in a gist/pastebin of both environments

borkdude22:09:31

there could also be some plugin in your leiningen profiles pulling in things

st3fan22:09:35

@quest i think i’m fine with my 5 requests/day 🙂

😁 4
awb9922:09:33

both machines use the SAME clojure version "1.10.0"

andy.fingerhut22:09:07

Most likely lein ring server-headless 5005 has a different dependency tree than lein repl or lein deps :tree commands.

andy.fingerhut22:09:42

Anyone know a way to get lein deps :tree output that matches what lein ring server-headless 5005 command would use?

borkdude22:09:58

lein with-profiles +ring deps :tree probably?

borkdude22:09:06

not sure if ring is a profile

noisesmith22:09:39

also if you construct your artifact using lein ring uberjar you will get the same deps there as lein ring gives you locally

awb9922:09:22

I am upgrading lein-ring plugin.

awb9922:09:26

I had an older version there.

awb9922:09:29

perhaps that was it..

borkdude22:09:12

not sure how to display deps tree with a lein plugin.

borkdude22:09:22

but problem solved it seems

dpsutton22:09:59

lein deps :plugin-tree

awb9922:09:59

Updated project.clj to new dependency,

awb9922:09:08

and it is downloading hte unify dll...

awb9922:09:15

so seems that lein ring plugin had this as dependency..

borkdude22:09:36

dll? are you on clojure CLR? 🙂

andy.fingerhut22:09:09

It is easy to stick with the terminology one first learned the concepts in 🙂

borkdude22:09:48

well, could be that this stuff is ported to CLR... I recently discovered a company in a nearby city which has been developing in clojureCLR for over 4 years and I hadn't heard of them before...

awb9922:09:21

It is working!!!

awb9922:09:45

I am on Java.

borkdude22:09:51

During the presentation I catched a glimpse of the guy's Explorer window displaying clojure.spec.alpha.dll ... which was very weird to me 🙂

andy.fingerhut22:09:54

You have successfully left dependency hell without having to enter any of the 2nd through 9th levels. Congratulations!

Alex Miller (Clojure team)01:09:11

9th level = Jackson version mismatch

borkdude22:09:11

@hoertlehner congrats! I'm off to bed now...

💤 4
awb9922:09:55

Thank you so much guys! I would not have been able to figure that one out myself! 🙂

borkdude22:09:11

that's why we're all on Slack 😉

👍 4