Fork me on GitHub
#clojure
<
2022-04-19
>
steffan10:04:10

@swlkr I think the automation for https://todayinclojure.com/ has stalled, the page has not updated since 16th April.

leifericf10:04:40

Oh! That’s nice. TIL this site exists.

☝️ 1
Gunnar21:04:09

Is this why? 😉 https://swlkr.com/posts/clojure-isnt-for-me/ And apparently the tool is written in janet and ruby! Interesting. But you all probably knew that and I'm not being useful.

Gunnar21:04:39

Perhaps slightly more useful, I just wanted to note that the GitHub automation still seems to be working. In other words, if anyone is really desperate to read it you can just read the index.html that is on the master branch. But if you also use a HTTP server then also the styles get loaded correctly: E.g.:

git clone  -b master
cd todayinclojure-static
python3 -m http.server 8080
... and click on the localhost link. git pull to update every day. 🙂 (Sorry for not knowing an equally compact Clojure HTTP serve snippet). A nice little tool indeed! (Thanks @swlkr, if you're around)

gabo22:04:23

I think since Java 11 you can do

java -m jdk.httpserver -p 8080
And since Java 18
jwebserver -p 8080

👍 1
Gunnar21:04:46

The site is updated again.

🎉 2
fabrao14:04:02

Hello all, what is vulnerability about CVE-2021-43138 and org.clojure/core.async? I could not figure out what is the .jar to replace.

dpsutton14:04:56

I found but it appears to be about https://github.com/caolan/async which is a javascript library for async programming

Alex Miller (Clojure team)14:04:13

it is not unusual to have false positives in the nvd checkers - if you are seeing that, you should file an issue so they can improve (I've done this in the past). might depend which tool you're using where to report that

fabrao14:04:28

I'm using

Alex Miller (Clojure team)14:04:42

at https://github.com/jeremylong/DependencyCheck/issues (used by nvd-clojure), you can file an issue - they have a template for false positives

fabrao15:04:52

Thank you Alex

Pavel Klavík15:04:28

Hi, I am trying to understand a strange behaviour of Safari on iOS together with Sente and Transit which sometimes incorrectly serialize messages: https://orgpad.com/img/Cb1U8vwilBZ7ozpDkuFplV/download?token=CPD6Xt0O5Lqa4IXGP2reS_

Pavel Klavík15:04:58

In the second message, :orgpage/id key is replaced by ^1.

Pavel Klavík15:04:37

The message itself is build in the code using map literal like this:

Pavel Klavík15:04:06

And the strangest thing is that when I run (js/console.log (pr-str message)) before sending it with Sente+Transit, the bug disappears.

dpsutton15:04:19

I think are you just seeing transit?

dpsutton15:04:22

❯ echo '[:orgpage/ops {:orgpage/id 1 :ops/op-id 2 :ops/window-id 3} {:orgpage/id 2 :ops/op-id 3 :ops/window-id 75}]' | jet -i edn -o transit
["~:orgpage/ops",["^ ","~:orgpage/id",1,"~:ops/op-id",2,"~:ops/window-id",3],["^ ","^1",2,"^2",3,"^3",75]]

dpsutton15:04:36

I believe this is just transit’s compression of repeated keys

Pavel Klavík15:04:22

In the second message, :orgpage/id is replaced by ^1 and when deserialized, I get nil key instead.

Pavel Klavík15:04:27

:orgpage/id is not repeated in the message.

Pavel Klavík15:04:46

Btw running (js/console.log message) does not fix the issue.

p-himik15:04:50

Are you reusing the writer but not reusing the reader?

p-himik15:04:54

Either reuse both or neither.

p-himik15:04:06

How it works, at least the way I see it from your screenshot: • You create a writer, write a message, and send it to a client • The client receives the message, creates a reader, reads the message, and discards the reader - thus discarding all information on already seen keys • You send a second message in the same way, reusing the writer - which at this point knows about the seen keys • The client creates a new reader and fails on ^1 because there are no keys that it has seen before

Pavel Klavík15:04:38

Why js/console.log fixes that?

p-himik16:04:38

No clue, I'd have to tinker with a minimal reproducible example to figure that out. Maybe you have some strange custom printers set up. Maybe your js/console.log is overwritten with something custom that does something relevant. Maybe your message has lazy collections in there and somehow that affects things (maybe unrolling those collections side-effects)`.

Pavel Klavík16:04:02

This is how I am using transit with sente: on the server:

Pavel Klavík17:04:00

and on the client:

Pavel Klavík17:04:25

not sure whether transit-packer is somehow reused on Safari, sounds plausible

p-himik17:04:33

But that's not a minimal reproducible example. :) Can you create a repo with all the components and a description of how to run it that would exhibit this behavior? I'm looking at the Sente code right now and it's a bit strange. On the one hand, I'm using it in pretty much the same way and have been using for a few years without noticing any issues. On the other hand, my memory of Transit internals must be wrong because they way Sente memoizes readers and writers is inconsistent and should lead exactly to the issues you see. I'll look a bit more into how exactly Transit does its caching, but you should still come up with an MRE. Even if I don't figure it out, it'll be incredibly useful when reporting a bug for Sente.

pinkfrog15:04:31

For the defn form, what’s the recommend place to add meta data, place a or b?

(defn (place-a) foobar  (place-b) [] body)

borkdude15:04:34

metadata for what? return value? then on the vector

pinkfrog15:04:55

For a function with multiarity,

(defn foobar
  ([] ...)
  ([x] ...))
How do we annotate the return type when the two arities have the same return type?

borkdude15:04:22

repeat it on both vectors

borkdude15:04:41

or if one arity calls the next one, then it doesn't matter much

borkdude15:04:46

then just on the last one

👍 1
pinkfrog15:04:11

for the form

(defn name doc-string? attr-map? ([params*] prepost-map? body) + attr-map?)
why isn’t it be
(defn name doc-string? (attr-map? [params] ...)

pinkfrog15:04:26

actually dunno what exactly

(defn name doc-string? attr-map? ([params*] prepost-map? body) + attr-map?)
means. regarding the attr-map? part.

borkdude15:04:58

https://clojure.org/reference/java_interop#typehints > For function return values, the type hint can be placed before the arguments vector:

pinkfrog15:04:50

I was referring to the

(defn name doc-string? attr-map? ([params*] prepost-map? body) + attr-map?)
The format is mysterious, why two attr-map? in the above form?

borkdude15:04:10

the latter attr-map? is an optional thing which nobody ever uses, except one library

pinkfrog15:04:17

The official doc has an attr-map,

Usage: (defn name doc-string? attr-map? [params*] prepost-map? body)
       (defn name doc-string? attr-map? ([params*] prepost-map? body) + attr-map?)
Sounds like the attr-map place is the recommended one.

Kelvin15:04:03

Speaking of metadata it seems that this is wrong?

(import '[org.apache.jena.graph Node NodeFactory])

(defn ^Node uri->node ;; Type hint for Jena Node instances
  "Create a URI node from `uri`."
  [uri]
  (NodeFactory/createURI uri))
I've always placed type hints for return values between the defn and function name, but the latest clj-kondo says this is wrong:
Prefer placing return type hint on arg vector: Node
clj-kondo(non-arg-vec-return-type-hint)

borkdude15:04:14

It doesn't say it's wrong, it just recommends one over the other

Kelvin15:04:33

Fair enough. So it seems like this recommended way for Clojure:

(defn var->node
  "Create a variable node from `var`."
  ^Node [var]
  ...)

👍 2
pinkfrog01:07:33

I feel like the above is wrong.

pinkfrog01:07:03

It should be either

(defn var->node {:tag Node} [var],,,)
or
(defn ^Node var->node [var] ,,,)

fadrian16:04:09

I've got a web-service A that depends on a second web-service B. A holds a connection to B and A exposes a REST interface to the world using compojure as the interface library. The question is how to manage the different servers - one can start or stop A and connect to/disconnect from B. What is the preferred way of ensuring that if A is running, a connection to B is available and if A shuts down, the connection to B gets closed?. I know that Component and Mount are two libraries that help to handle these sorts of issues, but I've no experience in using them and certainly no experience to choose between the two. I've also seen a couple of "roll your own" solutions using atoms and delays to work with. Does anyone have any advice for how to structure the code and which (if either) of Component/Mount to use?

kwladyka16:04:38

I don’t know your use case, but if you do some operations which needs to be done one by one consider use async channel. You can add there into queue requests without waiting for response and you will be sure there will be no parallel request. I did this for bookkeeping and warehouse things.

kwladyka16:04:38

It is not directly response for your question, but maybe can open your mind for other solution

kwladyka16:04:00

you can’t control connection A<->B, so I would try to send something to the third party service and if connection doesn’t work, then reconnect it. Without any extra code like component.

kwladyka16:04:09

similar logic to pool connections to DB

Noah Bogart16:04:42

has there been any interest in map literal shorthand notation, similar to how javascript's https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#new_notations_in_ecmascript_2015 works?

(def a 1)
(def b 2)
(def c 3)
;; current
(def m {:a a :b b :c c})
;; literal shorthand
(def m {a b c})

1
1
Derek16:04:39

I’ve seen a myriad of macros in this space

👍 1
isak16:04:48

If you also had d, how would you interpret it?

isak16:04:11

I think you'd need something more/else

Noah Bogart16:04:23

that's a great point, @U08JKUHA9, I didn't think about that lol

dpsutton16:04:00

there’s a good clojureverse thread on this. let me see if i can find it

isak16:04:03

In javascript I guess it is the commas. Would it be bad to make them significant in this one case?

👎 3
2
dpsutton16:04:07

(i like it. lots of people didn’t)

Noah Bogart16:04:09

if we were in another lisp (common lisp, racket, etc), adding a reader macro would handle this: #kw{a b c d} -> {:a a :b b :c c :d d} whereas #s{a b c d} -> {'a a 'b b 'c c 'd d}

isak16:04:59

Ideally it would be per entry, not something for the whole map.

lilactown16:04:01

you could add a reader macro for this in clojure. #kv [a b c d] could return a hashmap

lilactown16:04:38

the ambiguity of using the map literal + even number of forms is the real problem

👍 1
1
isak16:04:18

I don't see a way that can be solved as long as commas can't be significant

lilactown16:04:51

yeah, and that would be an extreme change in the informal spec that clojure abides by (commas are whitespace)

isak16:04:37

It is a potentially breaking change, though the codebases where that would be breaking are kind of bizzare.

isak16:04:25

It could just be a different character though. .? |?

dpsutton16:04:33

my understanding is that there is no chance to get a breaking change into the reader, and almost no chance to get an extension to the reader, especially for something that is just a shorthand or convenience. There are also lots of different design choices that make this unlikely to land in core. (same thing for dissoc-in collapsing empty subtrees, nils, empty maps, etc). So i would add a macro to your own codebase and see if you like it. But would basically not expect any movement from core

Noah Bogart16:04:17

yeah, that seems like an accurate assessment. thanks for the clojureverse link!

dpsutton16:04:58

if you make a macro and use it in your codebase for three months, post on http://ask.clojure.org and give the problem you solved, why it was great, and ask if something like that could go into core. If it is based on real world usage and solves a problem you will provide evidence. If its just off the cuff “wouldn’t it be nice if …” it will not lead anywhere most likely.

Noah Bogart16:04:41

hah no, I was more just thinking out loud and wondering what the communities thoughts on it were

isak16:04:54

This isn't really the kind of thing a macro would be satisfactory for, because requiring something all over the place is a pain in Clojure. Writing it out the long way would usually be faster than: 1. checking the requires of the namespace you are in 2. adding the macro if needed, 3. going back to where you were (I know some editors are able to help with this sometimes, but it is not reliable IME.) So I don't think there is a way to make this better without core support.

lilactown22:04:44

if you made it a reader macro, you wouldn't need to require it everywhere. just set up your data_readers.clj(c) in the root of your project

lilactown22:04:31

then you can also use #isak/kv [a b c d] syntax

👍 1
1
isak22:04:38

Can you do it per entry though, like JS? {a, b, c: "something"}

Noah Bogart23:04:49

I’m getting nerd sniped because i think the answer is yes

🙂 1
rayat05:04:43

For per entry, a suggestion:

#isak/kv { :& [ a b ] :c "something" }
obviously :& could be anything that is unlikely to be a key. Funny you should mention this, I was just wondering about this earlier today

thheller07:04:15

this whole tagged literal thing will not work. basically it will be calling a function with with vector with 4 values (not 4 symbols). it will not be the original form. in CLJS for some reason it is the form so it'll work there but not so in CLJ

Noah Bogart13:04:16

this works in clojure but idk if it's a good idea lol:

(defn kw-reader [values]
  (loop [[cur-val & next-vals] values
         results []]
    (cond
      cur-val
      (recur
        next-vals
        (if (symbol? cur-val)
          (-> results
              (conj (keyword cur-val))
              (conj cur-val))
          (conj results cur-val)))
      :else
      (->> results
           (partition 2 2 nil)
           (map vec)
           (into {})))))

(binding [*data-readers* {'nb/kw user/kw-reader}]
  (let [a :aaaa b :bbbbb d :ddddddd]
    #nb/kw [a b :c 1 d]))
;=> {:a :aaaa, :b :bbbbb, :c 1, :d :ddddddd}

isak14:04:57

What if you want the value of a to be the key for b's value? I think you'd need to complicate the syntax to resolve that ambiguity. But ultimately, even if you fix that, I don't think it is going to take off (even inside a single codebase) - it just won't be concise/convenient enough, there won't be editor support, etc. While in JS, basically everyone will be using that object syntax.

1
Noah Bogart15:04:11

oh yeah, this isn't good lol, just a proof of concept. poking at it, this gets significantly hairier if you want that

1
icemanmelting16:04:00

Hey guys, I am trying to write pretty fast to a clojure channel, by using the blocking syntax (>!!) Interrupted exceptions keep happening, have you guys gone through something like that?

:cause nil
 :via
 [{:type java.lang.InterruptedException
   :message nil
   :at [java.util.concurrent.locks.AbstractQueuedSynchronizer doAcquireSharedInterruptibly AbstractQueuedSynchronizer.java 1040]}]
 :trace
 [[java.util.concurrent.locks.AbstractQueuedSynchronizer doAcquireSharedInterruptibly AbstractQueuedSynchronizer.java 1040]
  [java.util.concurrent.locks.AbstractQueuedSynchronizer acquireSharedInterruptibly AbstractQueuedSynchronizer.java 1345]
  [java.util.concurrent.CountDownLatch await CountDownLatch.java 232]
  [clojure.core$promise$reify__8486 deref core.clj 7109]
  [clojure.core$deref invokeStatic core.clj 2320]
  [clojure.core$deref invoke core.clj 2306]
  [clojure.core.async$fn__5995 invokeStatic async.clj 175]
  [clojure.core.async$fn__5995 invoke async.clj 164]

hiredman17:04:44

The docs explain when an interrupted exception is thrown for that method

hiredman17:04:43

Sounds like the thread you are running on has its interrupt flag set

devn22:04:14

I have a question about an error message <thread>

devn22:04:30

(defn binary-search [nums target]
  (loop [left 0
         right (dec (count nums))]
    (when (<= left right)
      (let [m (quot (+ left right) 2)
            actual (get nums m)]
        (cond
          (= actual target) m
          (< actual target) (recur (inc m) right)
          :else (recur left (dec m)))))))

(binary-search (range 10) 4)

devn22:04:51

1. Unhandled java.lang.NullPointerException

Cannot invoke "Object.getClass()" because "x" is null

devn22:04:53

Now, I can get it to go away by doing

(binary-search (into [] (range 10)) 4)
but i’m curious about the error message

noisesmith22:04:10

get on range will always give you nil

devn22:04:33

derp, of course. i can’t brane good today

noisesmith22:04:35

user=> (get (range 10) 1)
nil

devn22:04:56

i even did that in my repl and then didn’t connect the dots. time to log off 😄

noisesmith22:04:58

I was just about to mention before you mentioned the vector case that get was the red flag here

noisesmith22:04:03

I think nth just fixes it, and for weird cases gives you a much more intelligible error

devn22:04:16

(nums m) would have given me a slightly better message i guess, nth better still

noisesmith22:04:29

of course using nth kind of makes using binary search as an algo silly

noisesmith22:04:59

probably best to fail if input isn't a vector or array

devn22:04:00

well, java.util.Collection has binarySearch in it, so writing one in the first place is suspect, but yes 😄

devn22:04:59

i was just puzzled because i don’t think i’ve ever seen Cannot invoke "Object.getClass()" because "x" is null before

devn22:04:22

and ive written a lot of clojure. achievement unlocked, i guess

dpsutton22:04:35

what’s the stacktrace of the NPE? Is that throwing in the equality check?

devn22:04:22

yep, let me grab the trace for ya

devn22:04:11

1. Unhandled java.lang.NullPointerException Cannot invoke “Object.getClass()” because “x” is null Numbers.java: 1095 clojure.lang.Numbers/ops Numbers.java: 253 clojure.lang.Numbers/lt REPL: 119 getclojure.extract/binary-search REPL: 111 getclojure.extract/binary-search REPL: 123 getclojure.extract/eval10259 REPL: 123 getclojure.extract/eval10259 Compiler.java: 7194 clojure.lang.Compiler/eval Compiler.java: 7149 clojure.lang.Compiler/eval core.clj: 3215 clojure.core/eval core.clj: 3211 clojure.core/eval interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn/fn AFn.java: 152 clojure.lang.AFn/applyToHelper AFn.java: 144 clojure.lang.AFn/applyTo core.clj: 667 clojure.core/apply core.clj: 1990 clojure.core/with-bindings* core.clj: 1990 clojure.core/with-bindings* RestFn.java: 425 clojure.lang.RestFn/invoke interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn main.clj: 437 clojure.main/repl/read-eval-print/fn main.clj: 437 clojure.main/repl/read-eval-print main.clj: 458 clojure.main/repl/fn main.clj: 458 clojure.main/repl main.clj: 368 clojure.main/repl RestFn.java: 1523 clojure.lang.RestFn/invoke interruptible_eval.clj: 84 nrepl.middleware.interruptible-eval/evaluate interruptible_eval.clj: 56 nrepl.middleware.interruptible-eval/evaluate interruptible_eval.clj: 152 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn AFn.java: 22 clojure.lang.AFn/run session.clj: 218 nrepl.middleware.session/session-exec/main-loop/fn session.clj: 217 nrepl.middleware.session/session-exec/main-loop AFn.java: 22 clojure.lang.AFn/run Thread.java: 833 java.lang.Thread/run

dpsutton22:04:54

huh. weird. can recreate with just (< 3 nil) funny there’s no nil check before getting the class for the number operations

devn22:04:11

right? glad I'm not the only one to look at my monitor sideways

dpsutton22:04:46

I wonder if that’s worth an http://ask.clojure.org question

dpsutton22:04:57

I wonder if there would be interest to clean up that stack trace

devn22:04:12

im gonna take a wild guess it's “because perf”

dpsutton22:04:02

that might be the right answer. But its in the static public boolean lt(Object x, Object y){ branch of Numbers.java. So its in Object territory so it might not be horrendous to add two null checks

devn22:04:20

will throw it up on ask, good suggestion

dpsutton22:04:20

i think its worth reporting on ask to see.

devn22:04:12

thanks to you both for having a peek

devn02:04:10

Perfectly reasonable, but if you have any color I'm curious about the cost/benefit. I threw it on Ask, so can reply there if that's preferable