https://twitter.com/quoll/status/1356422446488944642 Is is an intentional or an accidental behavior?
:or
is weird in a few ways, and is not intended to work inside sequential destructure
: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)
(let [{:or {foo (prn :foo)} :keys [foo]} {:foo :bar}])
will print :foo
aka CLJ-1676
Makes sense to me
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}
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
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.
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
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.
well, neither term is right here, should be sequential destructuring
or vector binding expressions (if you want to refer to the syntax)
stay tuned for some new sequential destructuring features in 1.11 though ... ;)
(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 ?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.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
(defn visit [x]
(println x)
x)
(w/prewalk visit [1]) ;; [1]
;; prints
;; [1]
;; 1
(w/postwalk visit [1]) ;; [1]
;; prints
;; 1
;; [1]
Tell me a project where i can show my cloj habilities to a HR recruiter.
thanks @smith.adriane that clears it
I guess u should add your example to clojuredocs .... it will be helpful to others too
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 ?
Check out clojure.walk/postwalk-demo
and clojure.walk/prewalk-demo
.
In one of the projects, I generate with it a nested table of contents for a Hiccup-based web page.
I used walk a bit for handle "unknow data sources", but now i use #specter
Can someone help me ?
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)
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. πdon't you have to use a doall
around the map
in create-all-tables
?
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.
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@dsp ? ^^^
Ah, bitten by laziness again. This makes sense. I'll see if I can force it! Thanks!
That indeed was exactly the problem. Wrapping with doall solved my issue. Thank you so much.
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
map does a bunch of work you don't need
ah perfect
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
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)))
"clojure.lang.LazySeq@7861"
user=>
imagine the second form there was just (map inc (range 3))
and you got back "() ;; because you haven't forced evaluation"
@vlaaad reveal
doesn't have anything to prompt a user about attempting to print a potentially infinite lazy seq, does it?
So I need to run a shell process and handle its output asynchronously line-by-line. What's the easiest way to do this?
Java's ProcessBuilder is pretty amenable to java interop
full access to the output stream
Any pointers on how I'd use ProcessBuilder to get a lazy seq or similar of strings? My Java is a bit rusty π
@noisesmith thanks!
i always check around for things like this: https://grep.app/search?q=doto%20%28ProcessBuilder
it prints (range)
forever until the end of times
and by end of times I mean and of memory
I have respecting *print-length*
on my todo-list...
Thanks.. although I'm not finding any good examples of what I'm trying to do there
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(and when the process does exit, it gives me an error)
Awesome, thanks!
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.
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
(a bit confusingly, the process stdout is actually an InputStream
from the POV of the Java code, and itβs obtained via getInputStream()
user=> (-> (ProcessBuilder. ["ls" "-l"]) (.start) (.getInputStream) (<http://clojure.java.io/reader|clojure.java.io/reader>) (line-seq) (first))
"total 2020"
it was likely waiting on the stdout being consumed while you were waiting for data on stderr