Fork me on GitHub
#clojure
<
2023-09-17
>
Alex Miller (Clojure team)14:09:07

Seems like it’s solving a problem we mostly don’t have. There’s not a lot of cases of on demand holder or dcl in compiled classes (I’m struggling to think of any), so not sure this would help anything. Var initialization is the important thing and that’s not final so not helped by this (constant dynamic added in 11 is more relevant there)

Ingy döt Net17:09:27

I'd like to format some generated clojure code. I was thinking of using zprint but it seems very heavy and pprint seems good enough:

user=> (pprint '(defmacro when "Evaluates test. If logical true, evaluates body in an implicit do." {:added "1.0"} [test & body] (list (quote if) test (cons (quote do) body))))
(defmacro
 when
 "Evaluates test. If logical true, evaluates body in an implicit do."
 {:added "1.0"}
 [test & body]
 (list 'if test (cons 'do body)))
nil
user=> (zprint '(defmacro when "Evaluates test. If logical true, evaluates body in an implicit do." {:added "1.0"} [test & body] (list (quote if) test (cons (quote do) body))))
(defmacro when
  "Evaluates test. If logical true, evaluates body in an implicit do."
  {:added "1.0"}
  [test & body]
  (list (quote if) test (cons (quote do) body)))
nil
user=> 
any advice?

p-himik17:09:24

What do you mean by "heavy"? Objectively though, zprint is very configurable.

jasonjckn17:09:50

(
  (:require
   [clojure.edn :as edn]
   [clojure.pprint :as pp]))

(defn pprint-code
  "Pretty print clojure code."
  [code-sexp]
  (let [process-file (with-out-str
                       (pp/write
                         (edn/read-string (str "[" code-sexp "]"))
                         :dispatch
                         pp/code-dispatch))]
    (subs process-file 1 (- (.length process-file) 1))))
Here's what I have, probably copied it from somewhere

Ingy döt Net18:09:02

re "heavy"

user=> (time (require 'clojure.pprint))
"Elapsed time: 0.229852 msecs"
nil
user=> (time (require 'zprint.core))
"Elapsed time: 4912.318311 msecs"
nil
user=> 

Ingy döt Net18:09:32

wth is it doing for 5 seconds?

p-himik18:09:50

Right, but is that time important for you? If yes, can't it be solved by AOT?

Ingy döt Net18:09:28

yes, and is it worth solving here?

Ingy döt Net18:09:06

Does pprint work reliably with code?

jasonjckn18:09:40

yes it's fine

jasonjckn18:09:56

you can also set pp/code-dispatch with with-pprint-dispatch , just realized

Ingy döt Net18:09:04

still wondering why zprint is taking so long here

jasonjckn18:09:37

there's some constants that may clip code, see implementation of pprint

(def ^{:private true} write-option-table
     {;:array            *print-array*
      :base             'clojure.pprint/*print-base*,
      ;;:case             *print-case*,
      :circle           'clojure.pprint/*print-circle*,
      ;;:escape           *print-escape*,
      ;;:gensym           *print-gensym*,
      :length           'clojure.core/*print-length*,
      :level            'clojure.core/*print-level*,
      :lines            'clojure.pprint/*print-lines*,
      :miser-width      'clojure.pprint/*print-miser-width*,
      :dispatch         'clojure.pprint/*print-pprint-dispatch*,
      :pretty           'clojure.pprint/*print-pretty*,
      :radix            'clojure.pprint/*print-radix*,
      :readably         'clojure.core/*print-readably*,
      :right-margin     'clojure.pprint/*print-right-margin*,
      :suppress-namespaces 'clojure.pprint/*print-suppress-namespaces*})

jasonjckn18:09:54

actually nvm, they're all nil by default , the ones that clip

p-himik18:09:55

> yes, and is it worth solving here? Only you can answer that since it depends on your needs. If pprint perfectly satisfies them, then using anything else is not worth it. pprint prints objects. If the code that you have can be represented in a [usefully] printable way, then pprint is fine. E.g. printing #inst "2023" will turn it into a much longer string.

Ingy döt Net18:09:43

I'll go with pprint for now at least. Thanks for the help 🙂

p-himik18:09:18

> still wondering why zprint is taking so long here It's just Clojure's compiler compiling all that code. Clojure's source code is quick to require exactly because it's been AOT'ed for you, and the results of that are in the JAR.

1
lread18:09:02

@U05H8N9V0HZ If you are really concerned about speed, you might also want to check out https://github.com/brandonbloom/fipp.

1
Nundrum18:09:31

Future/threading/chunking question. 🙏 I'm trying to start a future that uses babashka/process to read lines from stdin, process them, and swap! the results to an atom as they come in. That works fine, except for the chunking behavior. I'd like to take the laziness out of this, but can't figure out how. Code in thread.

1
Nundrum18:09:05

(def tcpdump-future                                                                                                                                                                                                                  
 (future (with-open [rdr (io/reader (:out tcpdump-stream))]                                                                                                                                                                         
           (binding [*in* rdr]                                                                                                                                                                                                      
             (while (nil? (:exit tcpdump-stream))                                                                                                                                                                                   
               (when-let [line (read-line)]                                                                                                                                                                                         
                 (swap! tcpdump-matches (comp vec #(remove nil? %) conj) (tcpdump-parse-line line))))))))

borkdude18:09:01

where do you see laziness in here?

Nundrum18:09:50

If I monitor @tcpdump-matches, it's empty until suddenly it has a bunch of entries.

Nundrum18:09:24

It seems to be getting chunked. I've tried a few variations, like loop/recur instead of while, but get the same behavior.

borkdude18:09:39

this may be because of buffering of io/reader

Nundrum18:09:30

Ahh I didn't consider that.

Nundrum19:09:47

MANY thanks for that! Passing a :buffer-size fixed it right up.

borkdude19:09:37

oh really, that's good to know!

borkdude19:09:42

perhaps we can document this?

Nundrum20:09:40

I suppose it is documented in so far as to/reader says it returns a BufferedReder 🙂 Strangely, when I try to replace the atom with a ref, it goes back to chunking.

(dosync (alter tcpdump-matches (comp vec #(remove nil? %) conj) (tcpdump-parse-line line)))
Maybe I need to go read up on core.async.

borkdude20:09:25

I've looked at the source of http://clojure.java.io and I don't see how setting buffer-size would affect your example

Nundrum22:09:06

Here's the whole thing. I can run that last deref over and over, and the ports show up in blocks. Just to test, I had a loop in bash just creating a new connection one a second for tcpdump to see. I must be doing something silly.

(def tcpdump-matches (ref []))

;16:31:18.577179 enp4s0 Out IP 192.168.2.101.58886 > 192.168.2.112.443: tcp 0
(defn tcpdump-parse-line [s]
  (let [[ts iface dir _ src _ dest proto _] (split s #" ")
        [_ _ _ _ destport]                  (split dest #"\.") ]
    (when (= "Out" dir) (read-string destport))))

(def tcpdump-stream
  (process {:err :inherit
            :shutdown destroy-tree}
           "sudo tcpdump -qn -iany '((tcp[tcpflags] & tcp-syn) != 0 or (ip6 and (ip6[13 + 40] & 2) == 2))'" ))

(def tcpdump-future
  (future (with-open [rdr (io/reader (:out tcpdump-stream) :buffer-size 1)]
            (binding [*in* rdr]
                (while (nil? (:exit tcpdump-stream))
                  (when-let [line (read-line)]
                     (dosync (alter tcpdump-matches (comp vec #(remove nil? %) conj) (tcpdump-parse-line line)))))))))

(deref tcpdump-matches)

borkdude08:09:35

What I meant is that :buffer-size isn't really an argument that gets handled in io/reader I believe