clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
borkdude 2021-03-03T12:56:23.491200Z

I am trying to extend transit for arrays which (surprisingly) are not supported out of the box. E.g.:

(def array-write-handler (transit/write-handler "pods/array" (fn [o] (vec o))))
(def array-read-handler (transit/read-handler (fn [o] (into-array o))))

(defn transit-json-read [^String s]
  (with-open [bais (java.io.ByteArrayInputStream. (.getBytes s "UTF-8"))]
    (let [r (transit/reader bais :json
                            {:handlers {"pods/array" array-read-handler}})]
      (transit/read r))))

(def array-type (type (into-array [])))
(def long-array-type (type (into-array Long [])))

(defn transit-json-write [^String s]
  (with-open [baos (java.io.ByteArrayOutputStream. 4096)]
    (let [w (transit/writer baos
                            :json
                            {:handlers {long-array-type array-write-handler
                                        array-type array-write-handler}})]
      (transit/write w s)
      (str baos))))
The problem here is that there is no generic array type on which :handler can dispatch? Or is there?

borkdude 2021-03-03T13:01:45.491600Z

I have trouble believing nobody has ever run into this before

borkdude 2021-03-03T13:10:35.491900Z

I think I'll just use a postwalk instead...

dnolen 2021-03-03T17:14:22.495600Z

anybody figured out workarounds for git deps and Apple M1?

ghadi 2021-03-03T17:43:59.496500Z

@dnolen what's the problem needing working around?

dnolen 2021-03-03T18:16:14.496700Z

git deps fail

ghadi 2021-03-03T18:19:30.497700Z

a repro with any error messages would be helpful

ghadi 2021-03-03T18:21:30.499100Z

I realize the error messages coming from that subsystem are not very useful, but e.g. is it just ssh: protocol, or https: too? is an ssh-agent active?

dnolen 2021-03-03T18:35:33.499600Z

Error building classpath. /private/var/folders/q4/3d296lg10qd4nt3sxxl98fmw0000gn/T/jna--541193536/jna3022155084463275496.tmp: dlopen(/private/var/folders/q4/3d296lg10qd4nt3sxxl98fmw0000gn/T/jna--541193536/jna3022155084463275496.tmp, 1): no suitable image found.  Did find:
	/private/var/folders/q4/3d296lg10qd4nt3sxxl98fmw0000gn/T/jna--541193536/jna3022155084463275496.tmp: no matching architecture in universal wrapper
	/private/var/folders/q4/3d296lg10qd4nt3sxxl98fmw0000gn/T/jna--541193536/jna3022155084463275496.tmp: no matching architecture in universal wrapper
java.lang.UnsatisfiedLinkError: /private/var/folders/q4/3d296lg10qd4nt3sxxl98fmw0000gn/T/jna--541193536/jna3022155084463275496.tmp: dlopen(/private/var/folders/q4/3d296lg10qd4nt3sxxl98fmw0000gn/T/jna--541193536/jna3022155084463275496.tmp, 1): no suitable image found.  Did find:
	/private/var/folders/q4/3d296lg10qd4nt3sxxl98fmw0000gn/T/jna--541193536/jna3022155084463275496.tmp: no matching architecture in universal wrapper
	/private/var/folders/q4/3d296lg10qd4nt3sxxl98fmw0000gn/T/jna--541193536/jna3022155084463275496.tmp: no matching architecture in universal wrapper
	at java.base/jdk.internal.loader.NativeLibraries.load(Native Method)
	at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:383)
	at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:227)
	at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:169)
	at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2383)
	at java.base/java.lang.Runtime.load0(Runtime.java:746)
	at java.base/java.lang.System.load(System.java:1857)
	at com.sun.jna.Native.loadNativeDispatchLibraryFromClasspath(Native.java:761)
	at com.sun.jna.Native.loadNativeDispatchLibrary(Native.java:736)
	at com.sun.jna.Native.<clinit>(Native.java:131)
	at com.jcraft.jsch.agentproxy.usocket.JNAUSocketFactory$CLibrary.<clinit>(JNAUSocketFactory.java:47)
	at com.jcraft.jsch.agentproxy.usocket.JNAUSocketFactory.open(JNAUSocketFactory.java:114)
	at com.jcraft.jsch.agentproxy.connector.SSHAgentConnector.open(SSHAgentConnector.java:93)
	at com.jcraft.jsch.agentproxy.connector.SSHAgentConnector.<init>(SSHAgentConnector.java:54)
	at com.jcraft.jsch.agentproxy.ConnectorFactory.createConnector(ConnectorFactory.java:104)
	at clojure.tools.gitlibs.impl$fn__1257.invokeStatic(impl.clj:31)
	at clojure.tools.gitlibs.impl$fn__1257.invoke(impl.clj:29)
	at clojure.lang.Delay.deref(Delay.java:42)
	at clojure.core$deref.invokeStatic(core.clj:2324)
	at clojure.core$deref.invoke(core.clj:2310)
	at clojure.tools.gitlibs.impl$call_with_auth.invokeStatic(impl.clj:51)
	at clojure.tools.gitlibs.impl$call_with_auth.invoke(impl.clj:43)
	at clojure.tools.gitlibs.impl$git_clone_bare.invokeStatic(impl.clj:73)
	at clojure.tools.gitlibs.impl$git_clone_bare.invoke(impl.clj:70)
	at clojure.tools.gitlibs.impl$ensure_git_dir.invokeStatic(impl.clj:112)
	at clojure.tools.gitlibs.impl$ensure_git_dir.invoke(impl.clj:102)
	at clojure.tools.gitlibs$resolve.invokeStatic(gitlibs.clj:33)
	at clojure.tools.gitlibs$resolve.invoke(gitlibs.clj:29)
	at clojure.tools.gitlibs$procure.invokeStatic(gitlibs.clj:47)
	at clojure.tools.gitlibs$procure.invoke(gitlibs.clj:41)
	at clojure.tools.deps.alpha.extensions.git$eval1323$fn__1325.invoke(git.clj:42)
	at clojure.lang.MultiFn.invoke(MultiFn.java:239)
	at clojure.tools.deps.alpha$expand_deps.invokeStatic(alpha.clj:422)
	at clojure.tools.deps.alpha$expand_deps.invoke(alpha.clj:390)
	at clojure.tools.deps.alpha$resolve_deps.invokeStatic(alpha.clj:495)
	at clojure.tools.deps.alpha$resolve_deps.invoke(alpha.clj:475)
	at clojure.tools.deps.alpha$calc_basis.invokeStatic(alpha.clj:655)
	at clojure.tools.deps.alpha$calc_basis.invoke(alpha.clj:629)
	at clojure.tools.deps.alpha.script.make_classpath2$run_core.invokeStatic(make_classpath2.clj:91)
	at clojure.tools.deps.alpha.script.make_classpath2$run_core.invoke(make_classpath2.clj:57)
	at clojure.tools.deps.alpha.script.make_classpath2$run.invokeStatic(make_classpath2.clj:119)
	at clojure.tools.deps.alpha.script.make_classpath2$run.invoke(make_classpath2.clj:113)
	at clojure.tools.deps.alpha.script.make_classpath2$_main.invokeStatic(make_classpath2.clj:169)
	at clojure.tools.deps.alpha.script.make_classpath2$_main.doInvoke(make_classpath2.clj:140)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:705)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.main$main_opt.invokeStatic(main.clj:514)
	at clojure.main$main_opt.invoke(main.clj:510)
	at clojure.main$main.invokeStatic(main.clj:664)
	at clojure.main$main.doInvoke(main.clj:616)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:705)
	at clojure.main.main(main.java:40)

alexmiller 2021-03-03T18:46:39.000100Z

Yeah, that’s just not going to work

borkdude 2021-03-03T20:31:37.000400Z

@dnolen Can you run this under Rosetta?

2021-03-03T20:33:24.001300Z

Hey all, can anyone help me make sense of this one:

(not (= ##NaN ##NaN))
=> true
(not= ##NaN ##NaN)
=> false

2021-03-03T20:36:18.002100Z

It’s basically part of the definition of NaN

2021-03-03T20:36:20.002300Z

http://github.com/jafingerhut/batman

2021-03-03T20:36:36.003100Z

“NaN is not equal to anything, including itself.”

2021-03-03T20:36:52.003300Z

Right, but that isn’t the issue.

2021-03-03T20:37:03.003600Z

Oh wait, not=

2021-03-03T20:37:04.003800Z

not= and (not (=))

2021-03-03T20:37:16.004300Z

Yeah, I’m catching up.

2021-03-03T20:38:31.005300Z

Here’s some more fun. I am working in a mixed clj/cljs env. Just discovered this:

(= (not (= ##NaN ##NaN)) (not= ##NaN ##NaN))
=> false
^In a clj repl
(= (not (= ##NaN ##NaN)) (not= ##NaN ##NaN))
=> true
^In a cljs repl

2021-03-03T20:38:41.005600Z

Ooooh, cljs

2021-03-03T20:39:01.006200Z

but the cljs version is the one that behaves as expected

2021-03-03T20:39:04.006400Z

The answer for Clojure (not ClojureScript) is probably somewhere in this annotated output: https://github.com/jafingerhut/batman/blob/master/doc/macos-10.13.6-jdk-adoptopenjdk-11.0.3-clojure-1.10.1.txt

2021-03-03T20:39:48.007Z

Yeah, I was looking at https://clojuredocs.org/clojure.core/not=, but that just the docs, not the src.

2021-03-03T20:40:10.007400Z

The source doesn’t really help:

(defn not=
  "Same as (not (= obj1 obj2))"
  {:tag Boolean
   :added "1.0"
   :static true}
  ([x] false)
  ([x y] (not (= x y)))
  ([x y & more]
   (not (apply = x y more))))

2021-03-03T20:40:30.008Z

For 2 args, not= just calls (not (= x y))

2021-03-03T20:40:35.008200Z

Here be dragons. Avoid NaN whenever you can.

💯 3
bronsa 2021-03-03T20:42:08.009300Z

= is inlined w/o boxing

bronsa 2021-03-03T20:42:12.009600Z

not= isn't

bronsa 2021-03-03T20:42:19.010100Z

user=> (not (= ##NaN ##NaN))
true
user=> (not (= (identity ##NaN) (identity ##NaN)))
false
user=> (not= ##NaN ##NaN)
false

2021-03-03T20:42:27.010400Z

IIRC, (= ##NaN ##NaN) can sometimes return true because of the optimization to check for identical? first, and sometimes two ##NaN objects are identical.

2021-03-03T20:43:06.011200Z

If two ##NaN objects are not identical, then (= ##NaN ##NaN) returns false

bronsa 2021-03-03T20:43:25.011700Z

indeed, when comparing ##NaN for equality as an object, it may return true

bronsa 2021-03-03T20:43:35.012100Z

but as primitive doubles, it will not

2021-03-03T20:44:08.013300Z

Ok, but for 2 args, not= just calls (not (= x y)), so why doesn’t it behave the same?

bronsa 2021-03-03T20:44:25.014100Z

because = has intrinsics in the compiler

2021-03-03T20:44:37.014600Z

There are some expressions here where the Clojure compiler inlines, vs. not, object vs. primitive double, etc. The transcript I linked above might cover all the possibilities, but it might not. Feel free to suggest additions if it is missing anything.

bronsa 2021-03-03T20:44:37.014700Z

which avoid boxing the arguments if they are literal primitive values

bronsa 2021-03-03T20:44:53.015300Z

but not= isn't special cased, so the primitive values always get boxed

bronsa 2021-03-03T20:45:20.016400Z

if not= was a macro instead of a function (or if it had an inline annotation), it'd behave differently

2021-03-03T20:45:30.016800Z

Ah, ok, the boxing is invoked in the process of evaling the arguments, and by the time they get to =, they’re already boxed.

2021-03-03T20:45:37.017200Z

That’s interesting.

bronsa 2021-03-03T20:45:39.017500Z

yes

2021-03-03T20:45:40.017600Z

There is almost no rule you can give about ##NaN behavior which is true in all cases, other than it will drive you nuts if you try to find general rules.

➕ 1
2021-03-03T20:46:40.018400Z

“The only way to win is not to play.” — WOPR.

bronsa 2021-03-03T20:46:49.018900Z

##NaN is the only value where this can happen

bronsa 2021-03-03T20:46:59.019300Z

and should just not be used in equality comparisons

2021-03-03T20:48:01.020400Z

Play with doubles/floats carefully, if you need them (read as much of https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html as you need for your purposes), but just stay away from NaN whenever you can.

2021-03-03T20:49:02.021500Z

A few more examples involving set elements and map keys: https://clojure.org/guides/equality#_floating_point_not_a_number

2021-03-03T20:49:50.022300Z

In my current use case, I definitely do not want ##NaN (or even doubles), but this does seem like a bug.

2021-03-03T20:50:04.022500Z

which part? 🙂

2021-03-03T20:50:20.022900Z

(not= not producing the same behavior as (not (=.

2021-03-03T20:50:57.023900Z

I haven't tried to examine what it would take to "fix" that, but I suspect it would mean slowing down operations on values that were not ##NaN

2021-03-03T20:51:18.024700Z

But it isn’t anything I’ll lose any sleep over. As we all say, the best solution is to prevent ##NaN in the first place.

bronsa 2021-03-03T20:51:22.024900Z

what it would take to fix this would be slowing down every call to = to check for isNan before identity?

bronsa 2021-03-03T20:51:25.025100Z

never gonna happen

bronsa 2021-03-03T20:52:22.026400Z

I suppose the ##NaN literal could return a new instance every time and that would "fix it" for the ##NaN literal

2021-03-03T20:52:29.026700Z

Yeah, I wasn’t even suggesting it be fixed. Mainly just wondering why it was behaving as it was in the first place.

Filipe Silva 2021-03-03T20:52:53.027300Z

well there's also this

Double/NaN
##NaN
(not= Double/NaN Double/NaN)
true
(not= ##NaN ##NaN)
false

2021-03-03T20:53:24.028200Z

That is probably because of Object identity in one case, but two non-identical objects in the other

➕ 1
bronsa 2021-03-03T20:53:27.028400Z

but there's still programmatic way to make it behave like this, it's just an edge case of how NaN work as a primitive (as defined by the float standard) and how it behaves as an object

2021-03-03T20:54:04.029100Z

##NaN eats babies for breakfast, shoots your dog, steals your Bible, and drives Darth Vader insane.

😂 2
2021-03-04T13:31:02.050500Z

Yes

🤯 1
Filipe Silva 2021-03-03T20:55:43.029400Z

'tis evil, I tell you

2021-03-03T20:56:01.029700Z

For sure.

2021-03-03T21:47:24.031500Z

floats remain one of the more interesting inventions of computer science. They are incredibly clever, and incredibly … sharp and pointy sometimes

bringe 2021-03-03T22:28:07.037900Z

https://clojure.org/reference/atoms Could someone explain to me why, in this example, the memoized fib call returns in a fraction of the time of the not memoized fib call even on the first call to the memoized fib? I ran the example and verified that it does run way faster on the first call, but how does this work if the result for those args hasn't been saved yet? I understand that subsequent calls with the same args should return much faster since the results have been saved, just unsure about how that happens with the first call.

bringe 2021-03-03T22:29:21.039400Z

Furthermore, if I run something like (fib 444) unmemoized it ties up the repl, naturally, but if I memoize fib then call the same thing I get an actual result in milliseconds. :thinking_face:

bringe 2021-03-03T22:29:43.039800Z

And that result hasn't been saved before ^

bringe 2021-03-03T22:32:00.040800Z

Maybe I should be clear I'm using clojurescript targetting node, not sure if that matters though.

p-himik 2021-03-03T22:32:01.040900Z

fib is recursive. (fib 5) calls (fib 4) and (fib 3). And if those are in cache, then (fib 5) will be fast. So now you have (fib 5) and (fib 4) in cache - exactly what's needed for (fib 6). And so on.

dpsutton 2021-03-03T22:32:11.041200Z

because fib calls itself. fib 35 calls fib 34 and fib 33. each of which call .... So "the first call" has lots of other first calls that are duplicated

bringe 2021-03-03T22:32:36.041400Z

Ohhhh 🙃

bringe 2021-03-03T22:32:41.041600Z

Thank you

p-himik 2021-03-03T22:33:09.042300Z

I strongly suggest reading the 2nd edition of SICP. It has some great material that deals with all sorts of problems in the functional world and outside of it - fib is one of them.

bringe 2021-03-03T22:33:19.042700Z

Oh I see! 😄 Thank you.

alexmiller 2021-03-03T22:33:50.043300Z

it's the difference between 444 and 444! :)

1
bringe 2021-03-03T22:34:28.043400Z

Oh cool. I actually have this on my list.

👍 2
2021-03-03T22:48:33.044100Z

I believe that most arithmetic operations are not associative using floating point arithmetic, at least for a large class of values, e.g. it is often the case that you get different results from ((a+b)+c) vs. (a+(b+c)). I believe Fortran and a few other programming language mandate that order of operations emitted by the compiler may not change from what is implied in the source code, so people can write floating-point numerical libraries and not have the compiler mess up what they intended.