Fork me on GitHub
#clojure
<
2021-02-02
>
souenzzo02:02:48

https://twitter.com/quoll/status/1356422446488944642 Is is an intentional or an accidental behavior?

alexmiller04:02:23

Makes sense to me

didibus04:02:52

This is intentional. When you destructure using [] you simply put symbols as variables where they belong. In your case, you've just put some weird symbols in it like :or, {c and 9}

didibus04:02:44

There's no further DSL about it where you can specify things like alternate values if nil, or give a name to the whole vector

didibus04:02:59

So it be more appropriate to say that its just there are less features around sequential destructuring than around associative. But there's no real "weird behavior" here, except maybe you assuming there'd be more features on offer.

didibus04:02:41

Not to say this can't be a common mistake, and it be nice to know if clj-kondo can warn against this, I suspect it might already

didibus04:02:47

It also bother me that they are referring to vectors as arrays 😛, Vectors are not the same as Arrays in Clojure, and the distinction matters a lot.

alexmiller04:02:22

well, neither term is right here, should be sequential destructuring

alexmiller04:02:53

or vector binding expressions (if you want to refer to the syntax)

alexmiller04:02:29

stay tuned for some new sequential destructuring features in 1.11 though ... ;)

👀 45
bananadance 9
noisesmith02:02:47

:or is weird in a few ways, and is not intended to work inside sequential destructure

mpenet12:02:22

:or will also always evaluate the values you pass to it, even if not needed, it's a bit counter-intuitive if you compare with (or x y)

mpenet12:02:30

(let [{:or {foo (prn :foo)} :keys [foo]} {:foo :bar}]) will print :foo

mpenet12:02:33

aka CLJ-1676

murtaza5208:02:39

(w/postwalk #(if (number? %) (inc %) (conj % 1)) [[1 2 3][4 5 6]])
=>[[2 3 4 1] [5 6 7 1] 1]
(w/prewalk #(if (number? %) (inc %) (conj % 1)) [[1 2 3][4 5 6]])
=>[[2 3 4 2] [5 6 7 2] 2]
trying to understand the diff between postwalk and prewalk. I understand one is postorder and the second is preorder traversal, however why do the results differ, just the traversal order is different, why should the result be different ?

phronmophobic08:02:59

consider a simpler example:

(w/prewalk #(if (number? %) (inc %) (conj % 1)) []) ;; [2]
(w/postwalk #(if (number? %) (inc %) (conj % 1)) []) ;; [1]
prewalk will visit the vector and conj, followed by inc ing the number just added postwalk will try to visit the children of the vector (it doesn't have any), then it will visit the vector which conj's the 1.

phronmophobic08:02:43

basically, prewalk will visit the parent node and visit the children of the node after the parent has been modified. postwalk will visit children before parents and the parent will have the modified children when the parent is visited

phronmophobic08:02:19

(defn visit [x]
  (println x)
  x)
(w/prewalk visit [1]) ;; [1]
;; prints
;; [1]
;; 1
(w/postwalk visit [1]) ;; [1]
;; prints
;; 1
;; [1]

Claudio Ferreira09:02:15

Tell me a project where i can show my cloj habilities to a HR recruiter.

murtaza5209:02:13

thanks @smith.adriane that clears it

murtaza5209:02:48

I guess u should add your example to clojuredocs .... it will be helpful to others too

p-himik09:02:29

Check out clojure.walk/postwalk-demo and clojure.walk/prewalk-demo.

murtaza5209:02:03

what is a practical application of postwalk/prewalk ? any real world examples of where it is more convenient to use prewalk/postwalk rather than clojure.core fns ?

p-himik09:02:18

In one of the projects, I generate with it a nested table of contents for a Hiccup-based web page.

👍 3
souenzzo11:02:12

I used walk a bit for handle "unknow data sources", but now i use #specter

Wellerson14:02:17

Can someone help me ?

Dominic Pearson15:02:56

I have a really weird issue. I don't understand what is happening. I'm using hugsql and sqlite. I've cut this down to a smaller snippet I can reproduce the strange behaviour with while also providing some context:

(def db {:classname "org.sqlite.JDBC"
         :subprotocol "sqlite"
         :subname (str (System/getProperty "user.home") "/.enki.db")})

(def tables #{"data"})

(def tables-loaded (into #{} (map #(let [p (str "enki/db/sql/" % ".sql")]
                                     (hugsql/def-db-fns p) %)
                                  tables)))

(defmacro create-all-tables
  "Create database tables. Expects each table listed in the `tables` set
  to have a corresponding create-`table`-table function defined."
  [x]
  `(map #((-> (str "create-" % "-table") symbol resolve) ~x) ~tables))
In sql/data.sql I have the following barebones hugsql def:
-- :name create-data-table
-- :command :execute
-- :result :raw
-- :doc Create data table
create table data (hash string, data blob);

-- :name create-data-index
-- :command :execute
-- :result :raw
-- :doc Create data index
create index index_hash on data(hash);
At REPL:
enki.db> (.delete (io/file (:subname db))) (create-all-tables db) (create-data-index db)
true([0])[0]

enki.db> (do 
           (.delete (io/file (:subname db)))
           (create-all-tables db)
           (create-data-index db))
Execution error (SQLiteException) at org.sqlite.core.DB/newSQLException (DB.java:1012).
[SQLITE_ERROR] SQL error or missing database (no such table: main.data)
Doing some macroexpand and debugging:
enki.db> (macroexpand '(create-all-tables db))
(clojure.core/map
 (fn*
  [p1__31878__31879__auto__]
  ((clojure.core/->
    (clojure.core/str "create-" p1__31878__31879__auto__ "-table")
    clojure.core/symbol
    clojure.core/resolve)
   db))
 #{"data"})

enki.db> (map #(-> (str "create-" % "-table") symbol resolve) tables)
(#'enki.db/create-data-table)

Dominic Pearson15:02:07

Something really weird is going on. Defining a fn at the REPL, with three should-be-independent actions:

enki.db> (defn delete-and-recreate-database []

           (let [database-file (io/file (:subname db))]
             (when (.exists database-file)
               (.delete database-file)))

           (let [x (create-all-tables db)] x)

           (create-data-index db))
#'enki.db/delete-and-recreate-database

enki.db> (delete-and-recreate-database)
Execution error (SQLiteException) at org.sqlite.core.DB/newSQLException (DB.java:1012).
[SQLITE_ERROR] SQL error or missing database (no such table: main.data)
When executed one by one, not in a funcall:
enki.db> (let [database-file (io/file (:subname db))]
           (when (.exists database-file)
             (.delete database-file)))
true

enki.db> (let [x (create-all-tables db)] x)
([0])

enki.db> (create-data-index db)
[0]
Really scratching my head. 😞

Tomas Brejla16:02:14

don't you have to use a doall around the map in create-all-tables ?

Tomas Brejla16:02:41

I just quickly skimmed through your code and it seems that create-all-tables will ouput a lazy sequence you don't touch in any way.. therefore it effectively has no need to actually execute the code you feed into map function.

Tomas Brejla16:02:17

of cource if you used this form in repl:

(let [x (create-all-tables db)] x)
..then it actually had to execute the code behind lazy sequence, as repl itself is trying to print the content of lazy sequence, which forces the code to be executed

Dominic Pearson16:02:18

Ah, bitten by laziness again. This makes sense. I'll see if I can force it! Thanks!

🤞 3
Dominic Pearson16:02:32

That indeed was exactly the problem. Wrapping with doall solved my issue. Thank you so much.

👍 3
noisesmith18:02:50

you can replace (doall (map f coll)) with (run! f coll) if you don't use the return value and there's just one collection arg

noisesmith18:02:04

map does a bunch of work you don't need

didibus21:02:15

This such a common issue, I wonder if it be beneficial to have nRepl not walk lazySeq to print them. So when a lazy-seq is returned to the REPL, it wouldn't realise it, which would make people realize they arn't doing it right

dpsutton21:02:59

that would make the "P" in REPL pretty weak with how Clojure is implemented. and it would be more like the far more annoying str on a lazy seq:

/p/clojure ❯❯❯ clj
Clojure 1.10.2
user=> (map inc (range 3))
(1 2 3)
user=> (str (map inc (range 3)))
"[email protected]"
user=>

dpsutton21:02:13

imagine the second form there was just (map inc (range 3)) and you got back "() ;; because you haven't forced evaluation"

p-himik21:02:19

@vlaaad reveal doesn't have anything to prompt a user about attempting to print a potentially infinite lazy seq, does it?

vlaaad21:02:20

it prints (range) forever until the end of times

vlaaad21:02:31

and by end of times I mean and of memory

vlaaad21:02:52

I have respecting *print-length* on my todo-list...

p-himik21:02:22

Awesome, thanks!

wombawomba21:02:05

So I need to run a shell process and handle its output asynchronously line-by-line. What's the easiest way to do this?

alexmiller21:02:04

Java's ProcessBuilder is pretty amenable to java interop

alexmiller21:02:36

full access to the output stream

wombawomba21:02:47

Any pointers on how I'd use ProcessBuilder to get a lazy seq or similar of strings? My Java is a bit rusty 😅

dpsutton21:02:10

i always check around for things like this: https://grep.app/search?q=doto%20%28ProcessBuilder

wombawomba21:02:03

Thanks.. although I'm not finding any good examples of what I'm trying to do there

wombawomba21:02:47

FWIW I tried to do

(first (line-seq (java.io.BufferedReader. (java.io.InputStreamReader. (.getErrorStream (.start (java.lang.ProcessBuilder ...))))))
but it doesn't seem to produce any output until the process exits

wombawomba21:02:30

(and when the process does exit, it gives me an error)

wombawomba22:02:05

Alright, I found another way to do what I wanted to accomplish 🙂 Will keep ProcessBuilder in mind for next time I want to do something like this.

Jeff Evans22:02:15

assuming the strings are the stdout of the process? you would need to use a separate thread to read that InputStream ex: https://github.com/streamsets/datacollector/blob/master/basic-lib/src/main/java/com/streamsets/pipeline/stage/executor/shell/ShellExecutor.java#L187

Jeff Evans22:02:41

(a bit confusingly, the process stdout is actually an InputStream from the POV of the Java code, and it’s obtained via getInputStream()

noisesmith23:02:51

@U15RYEQPJ

user=> (-> (ProcessBuilder. ["ls" "-l"]) (.start) (.getInputStream) () (line-seq) (first))
"total 2020"

🎉 3
noisesmith23:02:36

it was likely waiting on the stdout being consumed while you were waiting for data on stderr